Composition, Inheritance, and Aggregation in JavaScript

前端 未结 3 620
自闭症患者
自闭症患者 2020-12-12 12:16

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:

3条回答
  •  一向
    一向 (楼主)
    2020-12-12 12:42

    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.

提交回复
热议问题