Created
September 12, 2010 11:02
-
-
Save DmitrySoshnikov/575982 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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 <[email protected]> | |
* (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