JavaScript Inheritance – Are Super Methods Limited to Functional Inheritance?

inheritancejavascript

In Douglas Crockford's "JavaScript: The Good Parts", he walks through three types of inheritance: classical, prototypal, and functional. In the part on functional inheritance he writes:

"The functional pattern also gives us a way to deal with super methods."

He then goes on to implement a method named "superior" on all Objects. However, in the way he uses the superior method, it just looks like he is copying the method on the super object for later use:

// crockford's code:
var coolcat = function(spec) {
    var that = cat(spec),
        super_get_name = that.superior('get_name');
    that.get_name = function (n) {
        return 'like ' + super_get_name() + ' baby';
    };
    return that;
};

The original get_name method is copied to super_get_name. I don't get what's so special about functional inheritance that makes this possible. Can't you do this with classical or prototypal inheritance? What's the difference between the code above and the code below:

var CoolCat = function(name) {
    this.name = name;
}

CoolCat.prototype = new Cat();

CoolCat.prototype.super_get_name = CoolCat.prototype.get_name;
CoolCat.prototype.get_name = function (n) {
    return 'like ' + this.super_get_name() + ' baby';
};

Doesn't this second example provide access to "super methods" too?

Best Answer

var coolcat = function(spec) {
    var that = cat(spec),
        super_get_name = that.superior('get_name');

    that.get_name = function (n) {
        return 'like ' + super_get_name() + ' baby';
    };

    return that;
};

var cat = coolcat({ ... });

Can just as well be done with prototypes

var CoolCat = {
    constructor: function () {
        Cat.constructor.apply(this, arguments);
        return this;
    },
    getName: function () {
        return 'like ' + Cat.getName() + ' baby';
    }
};

var cat = Object.create(CoolCat).constructor({ ... });

Of course the main difference here is that your not creating two new functions every time you invoke coolcat. coolcat creates a new function for get_name and a new function for super_get_name and basically wastes memory left and right.

One may argue coolcat has privacy but it does not because anyone can call cat({}).superior('get_name'); and get your "private" method.

Of course if one wants privacy and prototypes that's perfectly possible, using the klass macro as shown in the article

var Cat = klass(function (privates) {
    return {
        constructor: function (name) {
            privates(this).name = name; 
        },
        getName: function () {
            return privates(this).name;
        }
    };
});

var CoolCat = klass(Cat, function (privates, $super) {
    return {
        constructor: function () {
            $super.constructor.apply(this, arguments);
        },
        getName: function () {
            return 'like ' + $super.getName.call(this) + ' baby';
        }
    };  
});

var cat = new CoolCat("someName");
console.log(cat.getName());

Live Example

Here we achieve both privacy and super class inheritance in an "elegant" manner.

In this elegant means not having the closure overhead of multiple functions per object (we still add a single closure per object for the privates function) and also allowing normal prototypical OO to just work.

Related Topic