The function constructor creates a new object and you have to do three additional things to assimilate conventional class/inheritance design:
- Call the base class: base.call(this, props...);
- augment the prototype: obj.prototype.func = function(){};
- re-assign the constructor
ECMA6 will introduce new syntax, like extend and Class, but JS is a prototypal language. This just means that every time you create an object var o = {}; It's not truly an empty object, it has no properties but sitting underneath in the dark waters of the compiler, a secondary object exists called the prototype, here the compiler links back to another object.
When you ask the compiler to get a value, e.g. console.log(this.x) it looks first at the objects own properties, if it can find it, it adorns a snorkel and dips its head into the tepid waters to search through the objects prototype. If its not there, the compiler must put on its wet-suit to dive deeper into the murky waters. If the compiler is now on the sea bed, it can search no more and its at the lowest level object called Object. The compiler will then cough and splutter back undefined as it gathers its breathe for another loop through the abyss.
So the closer your properties and functions are to the objects immediate context, the better your code will perform. If you try to chain many prototypes, the compiler will get the bends after the amount of diving and resurfacing it needs to do.
When you create a class in JS, you are creating instance properties and setting the prototype:
var Cat = function() {
this.age = 3;
this.colour = 'black';
};
Cat.prototype.age = function() { console.log(this.age); };
Using the new keyword creates a blank object and sets the context to this, so in Cat we're just augmenting the properties of the current context, e.g. this.age = 3;
The compiler already knows about the Cat.prototype and these functions are linked to your object and not copied verbatim to your object.
The new way to create objects is clunky and the following embraces prototypal delegation; call it mixins, extensions, inheritance or whatever, the whole point is that we can augment objects with other objects and put the functions on the prototype, so they are seen by all instances.
var manufacturer = {
id:'manufacturer',
constructor : function (args) {
this.boss = args.boss || 'Bernie Ecclestone';
this.country = args.country || 'UK';
return this;
}
};
var vehicle = {
id:'vehicle',
constructor : function (args) {
this.colour = args.colour || 'blue';
this.wheels = args.wheels || 2;
extend.call(this, manufacturer, args);
return this;
}
};
var driver = {
id:'driver',
constructor : function (args) {
this.name = args.name || 'John';
return this;
},
info : function () { console.log(this.name); }
};
var engine = {
id:'engine',
constructor : function (args) {
this.type = args.type || 'V6';
this.fuel = args.fuel || 'petrol';
return this;
},
tune : function () {
this.type = 'super-charged';
this.fuel = 'ethanol';
console.log('Car now ' + this.type + ' with ' + this.fuel);
}
};
var car = {
id:'car',
constructor : function (args) {
extend.call(this, vehicle, args);
extend.call(this, driver, args);
extend.call(this, engine, args);
return this;
},
info : function () {
console.log('boss: ' + this.vehicle.manufacturer.boss);
console.log('country: ' + this.vehicle.manufacturer.country);
console.log('driver: ' + this.driver.name);
console.log('colour: ' + this.vehicle.colour);
console.log('wheels: ' + this.vehicle.wheels);
console.log('type: ' + this.engine.type);
console.log('fuel: ' + this.engine.fuel);
console.log('\n');
}
};
function extend(proto, args){
this[proto.id] = Object.create(proto);
proto.constructor.call(this[proto.id], args);
}
var ferrari = Object.create(car).constructor({
boss: 'Maurizio Arrivabene',
country:'Italy',
name: 'Steve',
colour: 'red',
wheels: 4,
type:'100cc',
fuel:'diesel'
});
var lotus = Object.create(car).constructor({
name: 'Jenson Button'
});
var mclaren = Object.create(car).constructor({
type:'hybrid',
fuel:'battery/petrol'
});
ferrari.engine.tune();
ferrari.info();
/*
Car now super-charged with ethanol
boss: Maurizio Arrivabene
country: Italy
driver: Steve
colour: red
wheels: 4
type: super-charged
fuel: ethanol
*/
lotus.info();
/*
boss: Bernie Ecclestone
country: UK
driver: Jenson Button
colour: blue
wheels: 2
type: V6
fuel: petrol
*/
mclaren.info();
/*
boss: Bernie Ecclestone
country: UK
driver: John
colour: blue
wheels: 2
type: hybrid
fuel: battery/petrol
*/
panddefaultPersonis.Person.prototypepisdefaultPerson.