OK, OK, probably is just that I cannot wait to start writing stuff with [tinydown](https://github.com/WebReflection/tinydown#tinydown) but I tweeted about this after last #FlightNight and @angustweets demo ... so here what is behind [Flight](http://twitter.github.io/flight/) mixins choice.
### The Basics
JavaScript polymorphism is probably one of the best things you can find out there.
Not kidding, the flexibility that this language offers when it comes to context injection and runtime class definition is simply amazing, as simple, and amazing, is this idea:
```js
// most basic example
function enriched() {
this.method = function () {
// do stuff
};
}
```
Most common books and online documents usually describe above example as a _constructor with privileged methods_.
Well, that's actually what you get if you `var obj = new enriched();` but that's just one story.
Flight uses a different story, offering alternative methods with better semantics but basically going down to:
```js
enriched.call(object);
```
### Mixins Baby!
Using above pattern we can enrich any sort of object with the extra benefit of having a private scope the moment we invoke once the function.
This gives us the opportunity to pass arguments or simply have some private, shared in case of a prototype, variable too.
```js
// with arguments and private variables
var Mixin = function Mixin(arg0, argN) {
// private variables
var scoped = {};
// privileged methods
this.method = function method() {
return scoped;
};
// default properties or initialization
this.prop = arg0 || 'default';
this.other = argN;
};
// ========================================
// as Singleton
var singleton = new Mixin;
singleton.method(); // object
singleton.prop; // default
singleton.other; // undefined
// as prototype with defaults
function Class() {}
Mixin.call(Class.prototype, 'a', 'b');
var instance1 = new Class,
instance2 = new Class;
instance1.method(); // object
instance2.method(); // same object
// same methods
instance1.method === instance2.method; // true
// and same object
instance1.method() === instance2.method(); // true
instance1.prop; // a
instance1.other; // b
instance2.prop; // a
instance2.other; // b
```
I have realized how powerful is this simply considering the same approach with prototypes rather than each objects, where methods are of course created per each invokation of the mixin, but that would be indeed absolutely meant and expected.
What I am saying, is that if you are worried about too many closures and functions generated per each mixin, and you believe your mixin does not need any initialization and methods can be used anywhere without needing a private scope, here all you can do to follow same pattern and save a bit of memory:
```js
var Mixin = function(){
// created once and never again
function method1(){}
function method2(){}
function methodN(){}
return function () {
this.method1 = method1;
this.method2 = method2;
this.methodN = methodN;
};
}();
```
I guess that's it, right? But what if we need some sort of initialization per each mixin?
### Initializing A Mixing
There could be things we need to do to ensure the mixin will work as expected per each instance but if we want to coexist out there in the wild, we cannot name methods like `.init()` because of the name clashing if more than a mixin needs to be initialized ... are you following?
One possible solution that came into my mind is to prefix, with `init` the method, and use the mixin name as suffix.
```js
// defaults plus init
var Mixin = function Mixin(dflt0, dfltN) {
// private variables
var scoped = {};
// privileged methods
this.method = function method() {
return scoped;
};
// avoid multiple mixins name clashing
// prefix eventual initialization
// with 'init' plus mixinname
this.initMixin = function (arg0, argN) {
this.prop = arg0 || dflt0 || 'default';
this.other = argN || dfltN;
};
};
```
If we follow this approach, we can initialize any mixin we want at any time, e.g.:
```js
// explicit initialization
function Class(arg0, argN) {
this.initMixin(arg0, argN);
}
Mixin.call(Class.prototype, 'a', 'b');
var instance1 = new Class,
instance2 = new Class('different');
instance1.prop; // a
instance2.prop; // different
instance1.other; // b
instance2.other; // b
```
Per each instance we can then enrich the constructor so that every mixin that needs to be initialized at that time can be simply call the non clashing method.
#### Some Bad Idea You might Have
Well, it is common in JS to think about `for/in` loops and stuff so that if we know a prefix we can automate tasks ... but in this case I strongly discourage this approach unless you are not 100% sure all mixins initializations support same signature.
```js
// *DONT* automatic mixin initialization
function Class() {
for (var key in this) {
if (key.slice(0, 4) === 'init') {
this[key]();
// or
this[key].apply(this, arguments);
}
}
}
```
In few words, using above technique means coupling mixins with a specific, probably unknown in advance, class signature so it is strongly discouraged.
Sticking with an explicit mixin initialization is not only faster but able to create any sort of mixin signature.
### Events To The Rescue
As @kpk reminds me, [Flight is event based](https://twitter.com/kpk/status/323272363053551616) so there's also an easy way to do more after all mixins initialization: `this.after('initialize', fn);`
### As Summary
This is only one part of [Flight](http://twitter.github.io/flight/) where [**performance**](http://jsperf.com/mixin-fun/31) were strongly considered for all old and new browsers and this works.
In an era were many developers are looking for better classes this framework came out with such simple and powerful solution I can't believe I would like to have anything more than this for real apps!
Enjoy the Flight philosophy and embrace powerful simplicity any time you _A-HA_ it ;)
Comments
Post a Comment