Skip to content

Instantly share code, notes, and snippets.

@DmitrySoshnikov
Created September 12, 2010 11:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save DmitrySoshnikov/575982 to your computer and use it in GitHub Desktop.
Save DmitrySoshnikov/575982 to your computer and use it in GitHub Desktop.
/**
* Delegation based mixins for JavaScript.
*
* Non-specific extensions are used:
* __proto__
* __noSuchMethod__
*
* Tested in SpiderMonkey.
*
* Opened issues:
* - a mixin inherits from
* Object.prototype; a method may be
* found first there. It's may be avoided
* by setting __proto__ of a mixin
* to null, but then it's not possible
* to mixin other module to the mixin itself
* - reading inherited properties (not methods)
* of an object from the tailed prototype
* - ...
*
* @todo implement using Proxy objects and WeakMaps without
* non-specific extensions.
*
* @author Dmitry A. Soshnikov <dmitry.soshnikov@gmail.com>
* (C) Mit Style License
*/
/**
* mixins a module to an object
* @param {Object} module
* @param {Object} to
*/
function mixin(module, to) {
// convert to object if needed
// or create a new one
to = Object(to);
// real prototype of an object
var tailProto = to.__proto__ || Object.prototype;
// a proxy object to inject as a
// direct prototype of an object
var proxy = {
// first a mixed module is considered
// for searching a method
__proto__: module,
// and then the tail of the prototype chain
__noSuchMethod__: function (name, args) {
return tailProto[name].apply(to, args);
}
};
// inject the mixin module via proxy
to.__proto__ = proxy;
// proxied object
return to;
}
// a mixin module
var m = {
foo: function () {
alert(['m.foo', this === o]);
},
baz: function () {
alert(['m.baz', this === o]);
}
};
var o = mixin(m, {});
o.foo(); // "m.foo", true
alert(o.foo === m.foo); // true, delegation is used
// another mixin
var m2 = {
foo: function () {
alert(['m2.foo', this === o]);
},
bar: function () {
alert(['m2.bar', this === o]);
}
};
// mixin it too
mixin(m2, o);
o.foo(); // shadows "m", now "m2.foo", true
o.bar(); // "m2.bar", true
o.baz(); // "m.baz", from previous module "m"
// third mixin module
var m3 = {
test: function () {
alert(['m3.test', this === o, this === m2]);
}
};
// mixin it to another module
mixin(m3, m2);
o.test(); // "m3.test", true, false
m2.test(); // "m3.test", false, true
delete m2.foo; // remove shadowing "foo"
o.foo(); // again is taken from "m", "m.foo"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment