I wanted an easy way to augment objects, by adding functionality from any other object(s). More importantly, I needed a way to augment the object from multiple sources in a clean one-line solution.
The inheritance is done with this:
function extend(proto, args){
this[proto.id] = Object.create(proto);
proto.constructor.call(this[proto.id], args);
}
extend.call(this, vehicle, args); // one-liner
I can invoke as many objects as needed; with this pattern it's easy to swap and change the prototype chain, so I could just put the above extend code in the vehicle object, if that's where I want augmentation.
It's now easy to create a car by pulling in whatever you need:
extend.call(this, vehicle, args); extend.call(this, sunroof, args); extend.call(this, tyres, args); extend.call(this, wings, args); extend.call(this, rocket, args); etc...
Questions:
- Plausibility: is it flawed in some way?
- Optimisation: can I enhance the pattern?
- clunky: I had to create an
id
property for each object, so the extend function knows how to create a property on the executing context. It seems like a hack. Is there a way to get a prototype name from a named constructor?
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 */
function Foo() {}; Foo.prototype.bar = function() {}; var foo = new Foo();
? \$\endgroup\$