0

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?

8
  • var employeeInfo = new employee('Mike', 'Driver'); don't forget the new Commented Aug 30, 2017 at 15:11
  • hehe the whole trick is to imitate new :) Commented Aug 30, 2017 at 15:12
  • No, I think the whole point is to create an inheritance pattern, not to get rid of new.. If you want to get rid of new, there is something called the factory pattern.. Try putting new on two employee's, and you will see it works. Commented Aug 30, 2017 at 15:19
  • yes, to create an inheritance pattern but without new. I mean new is not the point here. It generally works without it. Adding new here doesn't affect anything since the function returns its own object that with prototype set, not the newly created one. Commented Aug 30, 2017 at 15:25
  • Ah, right see now. So your question is how would you say do super(name) using this approach?. Commented Aug 30, 2017 at 15:39

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

Comments

1

Since the func constructor completely disregards this, passing any context to it via call or apply will not work. Creating a way to copy over the super class' properties after creating an object is one of the ways you could accomplish your task.

// 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;
}

function copyProperties(source, target) {
  for (var prop in source) {
    if (source.hasOwnProperty(prop)) {
      target[prop] = source[prop];
    }
  }
}

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.