Search code examples
javascriptinheritanceprototype

Interesting JavaScript inheritance pattern


I have recently watched a video where Douglas Crockford was explaining inheritance patterns of Javascript. The video itself is pretty old - it was filmed 6 years ago - but still useful. In that video he showed one inheritance pattern he kinda invented (although I am not sure who the author is). This is the code using his approach:

// imitation of new operator
function objectConstructor(obj, initializer, methods) {
    // create prototype
    var func, prototype = Object.create(obj && obj.prototype);
    // add methods to the prototype
    if(methods) Object.keys(methods).forEach(function(key) {
        prototype[key] = methods[key];
    });
    // function that will create objects with prototype defined above
    func = function() {
        var that = Object.create(prototype);
        if(typeof initializer === 'function') initializer.apply(that, arguments);
        return that;
    }
    
    func.prototype = prototype;
    prototype.constructor = func;
    return func;
}

var person = objectConstructor(Object, function(name) {
    this.name = name;
},  {
    showName: function() {
        console.log(this.name);
    }
});
var employee = objectConstructor(person, function(name, profession) {
    this.name = name;
    this.profession = profession;
},  {
    showProfession: function() {
        console.log(this.profession);
    }
});
var employeeInfo = employee('Mike', 'Driver');
employeeInfo.showName(); // Mike
employeeInfo.showProfession(); // Driver

Unfortanately, he didn't show the invocation. So, this part

var employeeInfo = employee('Mike', 'Driver');
employeeInfo.showName();
employeeInfo.showProfession();

is mine. It generally works, but it turns out that I repeat this.name = name; for both "classes" - person and employee. I played around but I didn't manage to make it work properly without that repetition. Seems I cannot get name because such a property isn't contained in the prototypal chain for employee. I didn't succeed either in mixing in stuff like person.call(this, arguments). So, apart from whether it is cool/nice/smart/sensible etc. or not in 2017, how could I remove this.name = name; from employee and get the same result? Or everything is ok and this approach doesn't suppose it?


Solution

  • Here is your snippet with 2 small modifications so that you can do a super(name) type of call.

    I've placed comments were I've made the modifications.. with prefix keith:

    // imitation of new operator
    function objectConstructor(obj, initializer, methods) {
        // create prototype
        var func, prototype = Object.create(obj && obj.prototype);
        // add methods to the prototype
        if(methods) Object.keys(methods).forEach(function(key) {
            prototype[key] = methods[key];
        });
        // function that will create objects with prototype defined above
        func = function() {
            var that = Object.create(prototype);
            if(typeof initializer === 'function') initializer.apply(that, arguments);
            return that;
        }
        
        func.prototype = prototype;
        //keith: store the initialization in constructor,
        //keith: as func is already creating the object..
        prototype.constructor = initializer;
        return func;
    }
    
    var person = objectConstructor(Object, function(name) {
        this.name = name;
    },  {
        showName: function() {
            console.log(this.name);
        }
    });
    var employee = objectConstructor(person, function(name, profession) {
        //keith: call our super  person(name)
        person.prototype.constructor.call(this, name);
        this.profession = profession;
    },  {
        showProfession: function() {
            console.log(this.profession);
        }
    });
    var employeeInfo = employee('Mike', 'Driver');
    employeeInfo.showName(); // Mike
    employeeInfo.showProfession(); // Driver