HiveBrain v1.2.0
Get Started
← Back to all entries
snippetjavascriptMinor

Compose in Javascript - Go ahead with this example?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thisjavascriptwithexamplecomposeahead

Problem

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 Sou

Solution

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

Code Snippets

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

Context

StackExchange Code Review Q#36965, answer score: 2

Revisions (0)

No revisions yet.