61

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:

function Stock( /* object with stock names and prices */ ) {
    for (var company_name in arguments[0]) {
        // copy the passed object into the new object created by the constructor
        this[company_name] = arguments[0][company_name]; 
    }
}

// example methods in prototype, their implementation is probably redundant for
// this question, but list() returns an array with toString() invoked; total()
// adds up the stock prices and returns them. Using ES5 feature to make
// inherited properties non-enumerable 

Stock.prototype =  {
    list: function () {
        var company_list = [];
        for (var company_name in this)
            company_list.push(company_name);
        return company_list.toString();
    },
    total: function () {
        var price_total = 0;
        for (var company_name in this)
            price_total += this[company_name];
        return '$' + price_total;
    }
};

Object.defineProperties(Stock.prototype, {
    list: { enumerable: false },
    total: { enumerable:false }
});

var portfolio = new Stock({ MSFT: 25.96, YHOO: 16.13, AMZN: 173.10 });
portfolio.list();  // MSFT,YHOO,AMZN
portfolio.total(); // $215.19

(To make the code smaller, you can leave out the method implementations, like: Stock.total = function(){ /* code */ } I just put them in there to be fancy). If composition is favored for a lot of situations in OOP, how come most people using JavaScript seem to only use prototypes and inheritance? I did not find a lot of information about composition in JavaScript online, only in other languages.

Can someone give me an example using the above code to demonstrate composition and aggregation?

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
Brian
  • 1,571
  • 1
  • 15
  • 20
  • 1
    The question is too vague. Probably a better fit for [programmers.se](http://programmers.stackexchange.com). – Sergio Tulentsev Jan 02 '12 at 01:14
  • 1
    If it's too vague, then IMO it's not a good fit for any site. – Peter Olson Jan 02 '12 at 01:17
  • 4
    How could I be more specific? I present code using inheritance, now I'm interested in seeing it built using composition. There are other questions that are the same for other languages. – Brian Jan 02 '12 at 01:27
  • All composition is is an instance of something inside another instance, possibly with delegation to the composited class. What kind of example do you want? – Dave Newton Jan 02 '12 at 01:31
  • A great example of composition is the Phaser Game Framework (http://phaser.io) – bren Dec 30 '14 at 20:28

3 Answers3

78

The language is irrelevant when dealing with composition vs inheritance. If you understand what class is and what an instance of a class is, then you have all you need.

Composition is simply when a class is composed of other classes; or to say it another way, an instance of an object has references to instances of other objects.

Inheritance is when a class inherits methods and properties from another class.

Let's say you have two functionality, A and B. You want to define a third functionality, C, that has some or all of both A and B. You could either make C extend from B and A, in which case C has everything B and A has because C isA B and A, or you can make each instance of C have an instance of A and an instance of B, and invoke items on those functionalities. In the latter case, each instance C in effect wraps an instance of B and an instance of A.

Of course, depending on the language, you might not be able to have a class extend from 2 classes (e.g. Java doesn't support multiple inheritance), but that's a language specific detail that has nothing to do with the concept.

Now, for the language specific details...

I used the word class, but javascript has no notion of Class as such. It has objects, and thats it (other than the simple types). Javascript uses prototypal inheritance, which means it has a way of efficiently defining objects and the methods on those objects (this is the topic for another question; you can search SO as there are already answers.)

So going with our example above, you have A, B, and C.

For inheritance, you would have

// define an object (which can be viewed as a "class")
function A(){}

// define some functionality
A.prototype.someMethod = function(){}

If you wanted C to extend A, you would do

C.prototype = new A();
C.prototype.constructor = A;

Now every instance of C would have the method someMethod, because every instance of C "isA" A.

Javascript doesn't have multiple inheritance* (more on this later), so you can't have C extend both A and B. You can use composition, however, to give it the functionality. Indeed, this is one of the reasons composition is preferred by some over inheritance; there are no limits on combining functionality (but this isn't the only reason).

function C(){
   this.a = new A();
   this.b = new B();
}

// someMethod on C invokes the someMethod on B.
C.someMethod = function(){
    this.a.someMethod()
}

So there are your simple examples for both inheritance and composition. However, this is not the end of the story. I said before that Javascript does not support multiple inheritance, and in a sense it doesn't, because you can't base the prototype of an object off the prototypes of multiple objects; i.e. you can't do

C.prototype = new B();
C.prototype.constructor = B;
C.prototype.constructor = A;

because as soon as you do the third, line, you just undid the the second line. This has implications for the instanceof operator.

However, this doesn't really matter, because just because you can't redefine the constructor of an object twice, you can still add any methods you want to the prototype of an object. So just because you can't do the above example, you can still add anything you want to C.prototype, including all the methods on the prototypes of both A and B.

Many frameworks support this and make it easy. I do a lot of Sproutcore work; with that framework you can do

A = {
   method1: function(){}
}

B = {
   method2: function(){}
}

C = SC.Object.extend(A, B, {
   method3: function(){}
}

Here I defined functionality in object literals A and B, and then added the functionality of both to C, so every instance of C has methods 1, 2, and 3. In this particular case, the extend method (provided by the framework) does all the heavy lifting of setting up the prototypes of the objects.

EDIT -- In your comments, you bring out a good question, namely "If you use composition, how do you reconcile the scope of the main object against the scope of the objects of which the main object is composed".

There are a bunch of ways. The first is simply to pass arguments. So

C.someMethod = function(){
    this.a.someMethod(arg1, arg2...);
}

Here you are not messing with scopes, you are simply passing arguments around. This is a simple and very viable approach. (the arguments can come from this or be passed in, whatever...)

Another way to do it would be to use the call (or apply) methods of javascript, which basically allows you to set the scope of a function.

C.someMethod = function(){
    this.a.someMethod.call(this, arg1, arg2...);
}

to be a bit more clear, the following is equivalent

C.someMethod = function(){
    var someMethodOnA = this.a.someMethod;
    someMethodOnA.call(this, arg1, arg2...);
}

In javascript, functions are object, so you can assign them to variables.

the call invocation here is setting the scope of someMethodOnA to this, which is the instance of C.

hvgotcodes
  • 118,147
  • 33
  • 203
  • 236
  • `this.b.someMethod` in your example knows nothing about `this`. How will it calculate the total or list the stocks? – katspaugh Jan 02 '12 at 02:21
  • katspaugh, correct -- I corrected it. Banging this all out without actually running it... – hvgotcodes Jan 02 '12 at 02:39
  • katspaugh, `someMethod` on b does not know about `this` on A, that is true. However, you can write the method to accept general arguments. If you wanted to get really advanced, you could use call and apply; Ill update my answer – hvgotcodes Jan 02 '12 at 02:40
  • Thanks, I thought so. Then what's the point of all this OOP and inheritance, if the component prototypes are just name-spaced collections of generic utilities? They shouldn't have any state as such. Thus no need for constructors and prototypes. – katspaugh Jan 02 '12 at 02:47
  • If you use `call`/`apply` you loose the prototype chain with all its methods on the component instance. – katspaugh Jan 02 '12 at 02:50
  • 1
    katspaugh, yes thats true, you need to decide what's right for you based on the actual implementations you are dealing with. This is why software design is not trivial ;) – hvgotcodes Jan 02 '12 at 02:54
  • Thank you! I had a different idea of what composition was so I'm glad I came on here to clarify. – Brian Jan 02 '12 at 23:48
  • Well, now that I think of it, it makes no sense, but I thought it was a way to replace inheritance in single classes. I won't go into it because it won't benefit readers anyways, but instead of saying "composition vs inheritance" it should be "composition vs subclassing." Now that I know it's an alternative way to form a relationship or "extend" two or more classes, it makes sense. – Brian Jan 03 '12 at 08:57
  • 1
    Brian, but you are NOT extending a class when you use composition. When you inherit, the subclass has an `isA` relationship with the superclass. When you compose, that is not the case. It has implications in strongly typed languages.... – hvgotcodes Jan 03 '12 at 16:25
  • Well, by "extend" I meant, "additional functionality" but I shouldn't have used that term because apparently it's specifically used for inheritance lol. – Brian Jan 05 '12 at 23:51
  • Amazing response that helped me understand composition better. I just noticed there is not any reference if any differences or good practices for Aggregation in this context. – Shane Jan 29 '14 at 18:13
  • Best explanation of composition vs. inheritance I have come across. Thank you. – Alex Hill Jan 06 '15 at 18:34
  • ```C.prototype = new A();``` should now (i.e. since ES5) be avoided in favor of ```C.prototype = Object.create(A.prototype);```. – imrek Jul 13 '16 at 06:14
  • ```C.prototype.constructor = A;``` Why would you want to set the constructor of C ("subclass") to A ("superclass"). That's what automatically happens. You should use this statement to correct the constructor property of C (C.prototype.constructor) to the function C. Therefore, you should have ```C.prototype.constructor = C;``` – imrek Jul 13 '16 at 06:18
  • Also, before you do the assignment ```C.prototype = new A();``` you need to declare a ```function C() {}``` as you cannot set a property of an object that has not yet been defined. – imrek Jul 13 '16 at 06:28
4

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.

Glenn Mohammad
  • 3,871
  • 4
  • 36
  • 45
2

... Can someone give me an example using the above code to demonstrate composition and aggregation?

At first glance the provided example does not seem to be the best choice in order to demonstrate composition in JavaScript. The prototype property of the Stock constructor function still remains the ideal place for both methods total and list for both do access any stock object's own properties.

What can be done is decoupling the implementations of these methods from the constructors prototype and providing them back exactly there - yet in an additional form of code reuse - Mixins ...

example:

var Iterable_listAllKeys = (function () {

    var
        Mixin,

        object_keys = Object.keys,

        listAllKeys = function () {
            return object_keys(this).join(", ");
        }
    ;

    Mixin = function () {
        this.list = listAllKeys;
    };

    return Mixin;

}());


var Iterable_computeTotal = (function (global) {

  var
      Mixin,

      currencyFlag,

      object_keys = global.Object.keys,
      parse_float = global.parseFloat,

      aggregateNumberValue = function (collector, key) {
          collector.value = (
              collector.value
              + parse_float(collector.target[key], 10)
          );
          return collector;
      },
      computeTotal = function () {
          return [

              currencyFlag,
              object_keys(this)
                  .reduce(aggregateNumberValue, {value: 0, target: this})
                  .value
                  .toFixed(2)

          ].join(" ");
      }
    ;

    Mixin = function (config) {
        currencyFlag = (config && config.currencyFlag) || "";

        this.total = computeTotal;
    };

    return Mixin;

}(this));


var Stock = (function () {

  var
      Stock,

      object_keys = Object.keys,

      createKeyValueForTarget = function (collector, key) {
          collector.target[key] = collector.config[key];
          return collector;
      },
      createStock = function (config) { // Factory
          return (new Stock(config));
      },
      isStock = function (type) {
          return (type instanceof Stock);
      }
  ;

  Stock = function (config) { // Constructor
      var stock = this;
      object_keys(config).reduce(createKeyValueForTarget, {

          config: config,
          target: stock
      });
      return stock;
  };

  /**
   *  composition:
   *  - apply both mixins to the constructor's prototype
   *  - by delegating them explicitly via [call].
   */
  Iterable_listAllKeys.call(Stock.prototype);
  Iterable_computeTotal.call(Stock.prototype, {currencyFlag: "$"});

  /**
   *  [[Stock]] factory module
   */
  return {
      isStock : isStock,
      create  : createStock
  };

}());


var stock = Stock.create({MSFT: 25.96, YHOO: 16.13, AMZN: 173.10});

/**
 *  both methods are available due to JavaScript's
 *  - prototypal delegation automatism that covers inheritance.
 */
console.log(stock.list());
console.log(stock.total());

console.log(stock);
console.dir(stock);

There is a lot of information about composition vs inheritance online, but I haven't found decent examples with JavaScript. ...

I did not find a lot of information about composition in JavaScript online, only in other languages. ...

Maybe the search query was not specific enough but even in 2012 searching for "JavaScript Mixin composition" should have led into a not that bad direction.

... If composition is favored for a lot of situations in OOP, how come most people using JavaScript seem to only use prototypes and inheritance?

Because most of them use, what they got tought and/or what they are familar with. Maybe there should be more knowledge spread about JavaScript as delegation based language too and what can be achieved with it.

appendix:

This are related threads, recently updated and hopefully helping ...

Community
  • 1
  • 1
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37