Reputation: 4505
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?
Upvotes: 0
Views: 60
Reputation: 24221
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
Upvotes: 1
Reputation: 6112
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
Upvotes: 1