mauriblint mauriblint - 2 months ago 12
Javascript Question

javascript: extending methods between objects, like mixins

I want to share or reuse some logic between differents objects, that they will be pretty similar, just changing the "scope".

var Mixin = {
show: function () {
this.container.show();
},

hide: function () {
this.container.hide();
},

play: function (data) {
data.map().append();
}
};

var ObjectA = {
container: $('#container_a');

foo: function () {
this.play(otherData); // Mixin common method?
}
};

var ObjectB = {
container: $('#container_b'),

foo: function () {
this.play(data); // Mixin common method?
}
};


ObjectA.show() // show $('#container_a');
ObjectB.show() // show $('#container_b');


I was trying using underscore

_.extend(ObjectA, Mixin);


but it seems like I have issues with the reference of the Mixin (this reference to the last extended object), like if i need to clone the object and extend it?

Is there any approach to do something similar?

Thanks!!

EDIT: I having issue with the scope of 'this', that is referencing to window, when a pass as a callback a function inherits from the mixin, like this.

PersonMixin = {
mixinFoo: function () {
this.handleResponse();
}
};

Person = {
personMethod: function () {
OtherLibrary.libMehtod(this.mixinFoo);
}
};

Object.assign(Person, PersonMixin);


and then, something like this will fail, this an example stack trace

Person.personMethod();
OtherLibrary.libMethod(callbackMixin);
Ajax.post(callbackMixin);
callbackMixin(response); // this.handleResponse() is not defined, because this reference to window object.

Answer

You can do this in a number of ways, my preference is adjusting the objects __proto__ property on creation which will cause it to inherit your mixin via its prototype chain. This does not require the use of underscore.

I adjusted your example for ES6 and made it a bit simpler but should get the point across.

const PlayerType = (
  { show() {
      console.info(`show ${this.name}`)
    }
  , hide() {
      console.info(`hide ${this.name}`)
    }
  , play: function (data) {
      data.map().append();
    }
  }
)

const objA = { __proto__: PlayerType
, name: 'objA'
, foo(...args) {
    this.play(...args)
  }    
}

const objB = { __proto__: PlayerType
, name: 'objB'
, foo(...args) {
    this.play(...args)
  }    
}


objA.show()
objB.show()

Simpler and no ES6:

var Mixin = (
  { show() {
      console.info('show ' + this.name)
    }
  , hide() {
      console.info('hide ' + this.name)
    }
  }
)

var a = { __proto__: Mixin, name: 'a' }

var b = { __proto__: Mixin, name: 'b' }

a.show()
b.show()

Alternate - Does the same thing with Object.create().

var Mixin = (
  { show() {
      console.info('show ' + this.name)
    }
  , hide() {
      console.info('hide ' + this.name)
    }
  }
)

var a = Object.create(Mixin, { name: { value: 'a', enumerable: true } })

var b = Object.create(Mixin, { name: { value: 'b', enumerable: true } })

a.show()
b.show()

Comments