2

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.

EDIT 2: I can solve this issue using bind()

mauriblint
  • 1,802
  • 2
  • 29
  • 46

2 Answers2

2

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()
cchamberlain
  • 17,444
  • 7
  • 59
  • 72
  • Thanks @cchamberlain, can you add a super simple example not using ES6? :D – mauriblint Sep 09 '16 at 19:22
  • @mariblint - posted up a simpler example. If you hit F12 and plug either version into the browser console you should see they both work natively (unless you are a masochist using some legacy browser). – cchamberlain Sep 09 '16 at 19:28
2

It works, just check your syntax also.

var Mixin = {
 show: function() {
  console.log(this.tmp);
 }
}

var oA = {
 tmp: 'tmpA'
}

var oB = {
 tmp: 'tmpB'
}

var mA = Object.assign(oA, Mixin);
var mB = Object.assign(oB, Mixin)

mA.show();
mB.show()
top.dev
  • 447
  • 1
  • 6
  • 18
  • Thanks @top.dev, it seems with Object.assign is working. Do I need to create a new object? cannot extend the original oA and oB? – mauriblint Sep 09 '16 at 19:23
  • Actually in this case `mA` is equal to `oA`, so you can not create `mA`, just use `oA`, but if you want to have 'clean' `oA` for future you can do `var mA = Object.assign({}, oA, Mixin);` – top.dev Sep 09 '16 at 19:41
  • One difference between this approach and the one I posted is this creates two objects, then mutates them by assigning properties from Mixin to each object. My approach creates the two objects with Mixin as their prototype (no mutation occurs). – cchamberlain Sep 09 '16 at 19:43
  • var mA = Object.assign({}, oA, Mixin) dosn't mutate `oA`, and `Object.assign(oA, Mixin)` mutates `oA` – top.dev Sep 09 '16 at 19:45
  • Hi guys, sorry, I was implementing this approach, the Object.assign(), and I having issue with the scope of 'this' when a pass as a callback a function inherits from the mixin, i will edit the question to show an example. – mauriblint Sep 10 '16 at 14:34
  • 1
    I didn't understand your edited question. Read the post http://stackoverflow.com/questions/19277395/why-does-the-value-of-this-changes and `bind` method to change context – top.dev Sep 11 '16 at 13:12
  • yes, i can solve that using bind! exelent, thanks all you guys! – mauriblint Sep 16 '16 at 13:36