There is a lot of information about composition vs inheritance online, but I haven\'t found decent examples with JavaScript. Using the below code to demonstrate inheritance:
I think I can show you how to rewrite your code in "object composition" fashion by using plain JavaScript (ES5). I use factory functions instead of constructor functions for creating an object instance, so no new keyword needed. That way, I can favour object augmentation (composition) over classical/pseudo-classical/prototypal inheritance, so no Object.create function is called.
The resulting object is a nice flat-composed object:
/*
* Factory function for creating "abstract stock" object.
*/
var AbstractStock = function (options) {
/**
* Private properties :)
* @see http://javascript.crockford.com/private.html
*/
var companyList = [],
priceTotal = 0;
for (var companyName in options) {
if (options.hasOwnProperty(companyName)) {
companyList.push(companyName);
priceTotal = priceTotal + options[companyName];
}
}
return {
/**
* Privileged methods; methods that use private properties by using closure. ;)
* @see http://javascript.crockford.com/private.html
*/
getCompanyList: function () {
return companyList;
},
getPriceTotal: function () {
return priceTotal;
},
/*
* Abstract methods
*/
list: function () {
throw new Error('list() method not implemented.');
},
total: function () {
throw new Error('total() method not implemented.');
}
};
};
/*
* Factory function for creating "stock" object.
* Here, since the stock object is composed from abstract stock
* object, you can make use of properties/methods exposed by the
* abstract stock object.
*/
var Stock = compose(AbstractStock, function (options) {
return {
/*
* More concrete methods
*/
list: function () {
console.log(this.getCompanyList().toString());
},
total: function () {
console.log('$' + this.getPriceTotal());
}
};
});
// Create an instance of stock object. No `new`! (!)
var portofolio = Stock({MSFT: 25.96, YHOO: 16.13, AMZN: 173.10});
portofolio.list(); // MSFT,YHOO,AMZN
portofolio.total(); // $215.19
/*
* No deep level of prototypal (or whatsoever) inheritance hierarchy;
* just a flat object inherited directly from the `Object` prototype.
* "What could be more object-oriented than that?" –Douglas Crockford
*/
console.log(portofolio);
/*
* Here is the magic potion:
* Create a composed factory function for creating a composed object.
* Factory that creates more abstract object should come first.
*/
function compose(factory0, factoryN) {
var factories = arguments;
/*
* Note that the `options` passed earlier to the composed factory
* will be passed to each factory when creating object.
*/
return function (options) {
// Collect objects after creating them from each factory.
var objects = [].map.call(factories, function(factory) {
return factory(options);
});
// ...and then, compose the objects.
return Object.assign.apply(this, objects);
};
};
Fiddle here.