util.inherits - alternative or workaround

13,594

Solution 1

As of node version 5.0.0, util.inherits has been changed to support the behaviour you are looking for using the setPrototypeOf method:

FirstBase.js

function FirstBase(firstBaseProp){
    this.firstBaseProp = firstBaseProp;
}

FirstBase.prototype.getFirstBaseProp = function(){
    return this.firstBaseProp;
};


module.exports = FirstBase;

SecondBase.js

var FirstBase = require('./FirstBase.js'),
    util = require('util');

function SecondBase(firstBaseProp, secondBaseProp){
    this.secondBaseProp = secondBaseProp;
    SecondBase.super_.apply(this, arguments);
}

SecondBase.prototype.getSecondBaseProp = function(){
    return this.secondBaseProp;
};

util.inherits(SecondBase, FirstBase);

module.exports = SecondBase;

ThirdBase.js

var SecondBase = require('./SecondBase.js'),
    util = require('util');

function ThirdBase(firstBaseProp, secondBaseProp, thirdBaseProp){
    this.thirdBaseProp = thirdBaseProp;
    ThirdBase.super_.apply(this, arguments);
}

ThirdBase.prototype.getThirdBase = function(){
    return this.thirdBaseProp;
};

util.inherits(ThirdBase, SecondBase);

module.exports = ThirdBase;

instance.js

var ThirdBase = require('./ThirdBase.js');

var instance = new ThirdBase('first', 'second', 'third');

// With node < 5.0.0 (Object.create)
console.log(instance.getFirstBaseProp()); // first
console.log(instance.getSecondBaseProp()); // undefined
console.log(instance.getThirdBase()); // undefined

// With node >= 5.0.0 (Object.setPrototypeOf)
console.log(instance.getFirstBaseProp()); // first
console.log(instance.getSecondBaseProp()); // second
console.log(instance.getThirdBase()); // third

If you're running an older version of node that supports setPrototypeOf (0.12.x does), you can just export util.inherits and use it as an internal utility function.

Solution 2

It does not make sense that you have to declare your prototype after util.inherits(). My guess is util.inherits originated as an internal-use-only method, tailored only for the limited internal use-cases it was initially intended for, which at some point got published for general usage. The util module is written in pure JS, so it is very easy to implement your own version of util.inherit that preserves your prototype. Here's the original util.inherit source:

exports.inherits = function(ctor, superCtor) {
  ctor.super_ = superCtor;
  ctor.prototype = Object.create(superCtor.prototype, {
    constructor: {
      value: ctor,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
};

As for the multiple inheritance, that's going to be a much more difficult problem to tackle, as Javascript's prototype inheritance is not really suited for multiple inheritance at all. Each instance has only a single internal [[Prototype]] property, which is used to look up members that are not found in the actual instance. You could merge the prototypes of two separate "parent classes" into a single prototype, but you will then lose the inheritance to their parents, and you will lose the ability to change the parent prototype and have all children see the change.

Solution 3

You should first inherit, after the function definition, and then implement your object. Also don't forget to call the superclass constructor in your class's constructor.

Function A(y){
  this.y = x;
}

Function B(x,y){
  this.x = x;
  A.call(this,y);
}

util.inherits(B,A);

B.prototype.mythodA = function() {
    //do something
}

Solution 4

I very much wanted the same thing and was unhappy with extend, so I created this extension to the already useful util.inherits method:

var util = require('util');

module.exports = {
    inherits : function(sub, sup, proto) {
        util.inherits(sub, sup);
        if (typeof proto !== 'undefined') {
            Object.keys(proto).forEach(function(key) {
                sub.prototype[key] = proto[key];
            });
        }
    }
};

I put this in my project's ./util/index.js and then do this to use it:

var EventEmitter = require('events').EventEmitter;
var util = require('./util');

function Foo() {
    EventEmitter.call(this);
}

util.inherits(Foo, EventEmitter, {
    bar : function(){
        console.log(this instanceof EventEmitter); // true
    }
});

Maybe I'll publish this if I find it's robust and useful more and more. I just barely implemented it myself, so I'm giving it a test run.

Let me know what you think!

NOTE: This does override methods on either of the classes with the ones defined in the proto hash at the end. Just be aware of that.

Share:
13,594
André Alçada Padez
Author by

André Alçada Padez

I'm a coder, a developer, mostly for web projects. I'm constantly eager to learn and master new technologies, and my day doesn't end when i punch out the clock. I obsess with code and algorithms. I dream about them and sometimes i wake up with a totally new approach for an impossible problem i've been having. I love my life and wouldn't dream of doing anything else.

Updated on July 14, 2022

Comments

  • André Alçada Padez
    André Alçada Padez almost 2 years

    I am a n00b in node, and find util.inherits() very useful, except for the fact that it seems to replace the entire prototype of the original object. For instance:

    var myClass = function(name){
        this._name = name;  
    };
    
    myClass.prototype = {
        (...)
    };
    
    util.inherits(myClass, require('events').EventEmitter);
    

    seems to erase my original prototype.

    That brings me two inconveniences:
    1 - I have to declare add properties to my prototype after calling inherits,

    var myClass = function(name){
        this._name = name;  
    };
    
    util.inherits(myClass, require('events').EventEmitter);
    
    myClass.prototype.prop1 = function(){...};
    myClass.prototype.prop2 = function(){...};
    

    and, most important, i think i cannot inherit from two or more different classes.

    Can anyone explain to me why this makes sense and what would be a good way to work around this?

    Thanks

  • André Alçada Padez
    André Alçada Padez over 11 years
    ok, so is there a way to extend an object, similar to jQUery's $.extend?
  • André Alçada Padez
    André Alçada Padez over 11 years
    Thank you, i will try it when i have the chance
  • Sam
    Sam about 11 years
    You're missing the point here. This is the very thing he's trying to avoid (i.e., typing out B.prototype.xxx every time he wants to add another method to the prototype).
  • André Alçada Padez
    André Alçada Padez about 11 years
    Thanks, seems about right. Push it to github, so the community can try it. I can only try it next week but i will give you feedback. Thanks