3
\$\begingroup\$

I'm trying to use the good parts of Javascript and dont follow the classical inheritance. I studied and created this example of compose in Javascript, https://gist.github.com/fernandoperigolo/7789866.

My question is. You think this example have a good implementations of composition? Can i go ahead with it? If not, What you suggest?

(function(){
  // Adding compose to Function prototype
  Function.prototype.compose = function(argFn) {
    var fnToCall = this;
    return function() {
      // This line is really complex and i don't know exactly what this line do
      return fnToCall.call(this, argFn.apply(this,arguments));
    }
  }

  // Eletronic atributes and methods
  var asEletronic = function(){
    this.voltage = '220v';
    this.power = false;
    this.switchPower = function(){
      if (this.power === true) {
        this.power = false;
        console.info('Eletronic are off now.');
      }else{
        this.power = true;
        console.info('Eletronic are on now.');
      };
    }
    return this;
  };

  // Sound Reproducer atributes and methods
  var asSoundReproducer = function(){
    this.watts = 60;
    this.playSound = function(sound){
      console.info('Playing the sound: '+sound);
    };
    return this;
  };
  // All sound reproducer is a eletronic
  // Composing first level
  asSoundReproducer = asSoundReproducer.compose(asEletronic)

  // Micro System atributes and methods
  // Not used, just example
  var asMicroSystem = function(){
    this.cd = false;
    this.mp3 = true;
    return this;
  };

  // TV atributes and methods
  var TV = function(){
    this.pol = 42;
    this.hdmi = true;
    return this;
  };

  // Compose a TV with full resources
  // Compose second level, asSoundReproducer is a composed object
  // This line looks me strange...
  TV = TV.compose(asSoundReproducer);

  // Create a new TV
  var myTV = new TV();

  // Looking my TV
  console.log(myTV);

  // Call a Eletronic method
  myTV.switchPower();

  // Call a Sound Reproducer method
  myTV.playSound('guitar.mp3');
})();
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Interesting question,

from your comments inside the code you seem to not have written the code or have heavily borrowed from somebody else's code..

From a high level I have only a few misgivings:

  • Do not modify standard JS objects, so do not add compose to the prototype of Function, I grant you that it looks cool but it will bite you at some point
  • myTV instanceof asSoundReproducer does not work, so figuring what kind of object you are dealing with can be painful ( this is a drawback from not using standard js OO )
  • You have some provision for passing parameters to functions with argFn.apply(this,arguments) but the function signatures might look ugly since you do not cut out argFn from arguments
  • When reading up on composing, I prefer much to be able to compose in one go like TV = compose( asSoundReproducer, asMicroSystem );
  • I am not sure what as stands for in your function names, I would drop anything that resembles Hungarian notation.

This counter proposal is more complicated, but handles multiple constructors in 1 go, amd provides support for parameters and typeof.

function merge(object, boltOn) {
  //Simply merge the properties of boltOn into object, overriding existing properties
  for(var property in boltOn)
    if(boltOn.hasOwnProperty(property))
      object[property] = boltOn[property];
  return object;
}

function compose(/*Constructor1, Constructor2, ..*/) {
  //Keep a closure reference for later
  var constructorReferences = arguments;

  return function ComposedConstructor(/*parameter1, parameter2, ..*/) {

    //Clone the constructors, we will modify the clones
    var constructorClones = Array.prototype.slice.call(constructorReferences).map(function(Constructor) {
      return new Object(Constructor);
    });

    var constructor = constructorClones.pop();
    //Set up the prototype chain, without loosing the original prototypes thru `merge`
    while(constructorClones.length) {

      var nextConstructor = constructorClones.pop();
      nextConstructor.prototype = merge(new constructor(arguments), nextConstructor.prototype);
      constructor = nextConstructor;

    }
    //Call the first constructor with the arguments provided to the constructor
    constructor = constructor.bind.apply(constructor, [null].concat(Array.prototype.slice.call(arguments)));
    return new constructor();
  };
}


function A(name) {
  this.a = name;
  console.log(arguments);
}
A.prototype.sayA = function() {};

function B() {
  this.b = 2;
  console.log(arguments);
}
B.prototype.sayB = function() {};

function C() {
  this.c = 3;
  console.log(arguments);
}
C.prototype.sayC = function() {};

var O = compose(A, B, C);
var o = new O('Samsung');

// {[object Object] { a: "Samsung",b: 2,c: 3,sayA: function,sayB: function,sayC: function}
console.log(o); 
console.log(o instanceof A); //true
console.log(o instanceof B); //true
console.log(o instanceof C); //true
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.