Another Approach To Mixins (First Draft)
========================================
Synopsis
--------
We discuss a different style of Javascript mixins, one that creates a mixin
function for a given API and a particular initialization function. It is
both simpler to use and more flexible than many compteting techniques.
The Problem
-----------
Mixins in Javascript have been [discussed often][gs1]. There are many
implementations, although the basic patterns are much the same. But there has
always been something unsatisfying about any of them, and I have been struggling
to figure out a way to handle the conflicting pressures that mixins demonstrate.
[gs1]: https://www.google.com/search?q=javascript+mixin
On a recent bus ride I scratched out a possible solutions for this (pencil and
paper -- yes, I'm that old!) and put it aside until I ran across Peter Michaux's
[new article][pm1]. That made me want to see if my idea could work in practice.
So I've tried it, and I think it's worth sharing.
[pm1]: http://peter.michaux.ca/articles/mixins-and-constructor-functions
Peter's article used the constructor `Observable`, and I will stick with that
for most of my examples. Here is one version of that constructor:
> var Observable = function() {
> this.observers = [];
> };
> Observable.prototype.observe = function(observer) {
> this.observers.push(observer);
> };
> Observable.prototype.notify = function(data) {
> for (var i = 0, ilen = this.observers.length; i < ilen; i++) {
> this.observers[i](data);
> }
> };
A mixin takes an API like `Observable` and adds it to a different object, often
to the prototype for another constructor function. The API can be arbitrary, but
there is one caveat with most mixin techniques: if any initialization is
necessary for the API to work, it needs to be run on all instances of the
mixin target. Thus if we have a constructor like the following:
> var Person = function(name) {
> this.setName(name);
> }
>
> // somehow mixin Observable to Person
>
> Person.prototype.setName = function(name) {
> var oldName = this.name;
> this.name = name;
> this.notify({oldName:oldName, newName:this.name});
> }
We will have a problem with this:
> var person = new Person("steve");
because it will be looking for `this.observers`, which won't exist. One
technique that I've used before, and the one that Peter's article suggests, is
to add a call to the `Observable` constructor inside the `Person` constructor:
> var Person = function(name) {
> Observable.call(this);
> this.setName(name);
> }
This works fine, but it causes us to have to know about the mixin when writing
the constructor. In this case, that's not an issue, since the `Person` prototype
already uses one of the mixin methods (`notify`) but it's an odd intrusion in
the general case.
I would really like a technique that handles this for us. The problem is that
when we mix our API into a constructor, there is no actual instance for us to
modify. There seems to be no opportunity to run the initialization code except
inside that constructor, unless we accept the ugly possibility of wrapping the
work of each of our mixin's methods inside `if`-blocks that check to see if the
initialization has been run, running it if not. We like clean code; we really
don't want that. For a long time, I thought this was an intractable issue, but
I've recently been working with an approach that seems to solve it.
A Possible Solution
-------------------
What if we could run some one-time initialization exactly in the case that our
mixin API is accessed? That would take care of this thorny issue.
It turns out that this is not too hard to do. It's simply an extension of the
notion of lazy-loading of functions, a topic well-covered in another excellent
[article][pm2] by Peter Michaux. The only real difference is that in our case,
instead of replacing a single function, we replace all the functions from the
mixin API. Here's one implemenation of this concept:
> var createMixinApi = function(api, initFn) {
> var name, result = {};
> var createRealApi = function() {
> var method = Array.prototype.shift.call(arguments);
> initFn.call(this);
> for (name in api) {if (api.hasOwnProperty(name)){
> this[name] = api[name];
> }}
> return this[method].apply(this, arguments);
> };
> for (name in api) {if (api.hasOwnProperty(name)) {
> result[name] = (function(name) {
> return function() {
> Array.prototype.unshift.call(arguments, name);
> return createRealApi.apply(this, arguments);
> }
> }(name));
> }}
> return result;
> };
[pm2]: http://michaux.ca/articles/lazy-function-definition-pattern
## Explanation ##
That's a bit of a mouthful. Let's break it down a bit. Here is the actual
function that will be mixed in to the target as the property whose name is
represented by the variable `name`:
> function() {
> Array.prototype.unshift.call(arguments, name);
> return createRealApi.apply(this, arguments);
> }
So after calling this:
> extend(Person.prototype, createMixinApi(Observable.prototype, Observable))
Person.prototype would look something like this:
> {
> setName: function(newName) {
> /* existing implementation */
> },
> observe: function() {
> Array.prototype.unshift.call(arguments, name);
> return createRealApi.apply(this, arguments);
> },
> notify: function() {
> Array.prototype.unshift.call(arguments, name);
> return createRealApi.apply(this, arguments);
> }
> }
This does not look too promising. Obviously `observe` and `notify` should not
have the same exact implementation. But in fact they don't. They are bound to
different closures. And the `name` variable in the two functions is bound
differently in their closures (to 'observe' and 'notify'.) When we call the
functions, this makes all the difference.
Once one of these functions is invoked on the `Person` instance, though, the
initialization is run, and all of them are replaced with the actual API of
`Observable`. If you understand how that happens, feel free to skip the next
section.
## Code Walkthrough ##
We'll examine in detail what happens when we do this:
> var person = new Person("Steve");
The `Person` constructor is invoked with the parameter "Steve". This calls the
prototype `setName` function, passing "Steve". The third line of that function
calls the `notify` method passing an object with the old and new names. This is
the first usage of the mixed in code, and it's where things get interesting.
We call the prototype `notify` function, and when we enter this function, we have
these variables available:
Name | Defined in | Value
----------------|--------------------------|----------------------------------------------
arguments | local function scope | ["Steve"] (an Arguments object, not an array)
this | local function scope | `Person` object
name | outer closure | "notify"
api | inner closure | {notify: [a function], observe: [a function]}
createRealApi | inner closure | [a function]
initFn | inner closure | The `Observer` constructor function
<del>name</del> | <del>inner closure</del> | <del>"notify"</del>
Note that the `name` property from the inner closure is shadowed by the `name`
property from the outer closure. Although they have the same value right now,
that is only because the order they were iterated in the order they appeared in
the original `Observable` definition. (Had `observe` been defined after
`notify`, then `name` in the inner closure would now have the value "observe".)
Now we unshift the value `name` onto the `arguments` pseudo-array and call the
`createRealApi` function. Inside that function, here are the variables:
Name | Defined in | Value
----------------|--------------------------|----------------------------------------------
arguments | local function scope | ["notify", "Steve"]
this | local function scope | `Person` object
api | closure | {notify: [a function], observe: [a function]}
createRealApi | closure | [a function]
initFn | closure | The `Observer` constructor function
name | closure | "notify"
In this function, we pull the method name out of `arguments` and call the
initialization function we supplied, which in this case is the `Observable`
constructor. That is called in the context of our new `Person` object. (This is
the step I didn't want to do inside the `Person` constructor.)
Then we add all the functions from the mixin to our instance. Note that these
functions will shadow the ones in the `Person` prototype. At this point, we now
have the initialization of `Observable` completed inside our `Person` instance.
All that remains is to complete the call we were trying to make in the
beginning, which is accomplished by this line:
> return this[method].apply(this, arguments);
Refinements
-----------
The API This presents is still rather clunky. To actually use it, we need to
depend on an external `extend` function, and we don't have a great location to
store our new mixin API.
The latter problem has a reasonable solution: Why not store it as a property of
the `Observable` constructor itself? As to the former problem, Angus Croll makes
a [good case][ac1] for thinking of such mixin APIs as functions rather than as
static objects. Combining these, we would like an API That looks something like this:
[ac1]: http://javascriptweblog.wordpress.com/2011/05/31/a-fresh-look-at-javascript-mixins/
> Observable.mixinTo = createMixin(Observable.prototype, Observable);
>
> // ... and then later ...
>
> Observable.mixinTo(Person.prototype);
We can turn the above into such a reusable function-generator in a fairly
straightforward manner:
> var createMixin = (function() {
> var extend = function(destination, source) {
> for (var name in source) {if (source.hasOwnProperty(name)) {
> destination[name] = source[name];
> }}
> return destination;
> }
> var createMixinApi = function(api, initFn) {
> var name, result = {};
> var createRealApi = function() {
> var method = Array.prototype.shift.call(arguments);
> initFn.call(this);
> for (name in api) {if (api.hasOwnProperty(name)){
> this[name] = api[name];
> }}
> return this[method].apply(this, arguments);
> };
> for (name in api) {if (api.hasOwnProperty(name)) {
> result[name] = (function(name) {
> return function() {
> Array.prototype.unshift.call(arguments, name);
> return createRealApi.apply(this, arguments);
> }
> }(name));
> }}
> return result;
> };
> return function(api, initFn) {
> var mixin = createMixinApi(api, initFn);
> return function(destination) {
> return extend(destination, mixin);
> };
> };
> }());
There is still one bit of clean-up that remains. For our current example, and
probably for many others, the initialization function and the API to mixin are
intimately related, as the former is a constructor function and the latter its
prototype. There is definitely a bit of code smell to repeating the construtor
name in this:
> Observable.mixinTo = createMixin(Observable.prototype, Observable);
I probably would not want to eliminate the first use of `Observable` because,
while it might sometimes be convenient to simply call a function passing a
constructor as a parameter, the code would be considerably less readable to
someone who didn't know what the Mixin function did. But the other two are
clearly repetitive. It would be cleaner, in this case, to simply call:
> Observable.mixinTo = createMixin(Observable);
But there are other ways we would want to use this, ways in which the
relationship between the API to mix in and initialization function is not so
simple. I'll explain a few below. This means we wouldn't want that version of
the function to be the only version available. To deal with this conflict,
we could present any of these variations:
> Observable.mixinTo = Mixin.create(Observable);
> var someOtherMixinFunction = Mixin.create(someAPI, someInitializater);
or
> Observable.mixinTo = Mixin.create(Observable);
> var someOtherMixinFunction = Mixin.createApi(someApi, someInitializater);
or
> Observable.mixinTo = createMixin(Observable);
> var someOtherMixinFunction = createMixin.forApi(someApi, someInitializater);
The first variation seems very popular these days in Javascript. The jQuery API,
for instance, uses a great deal of method overloading. I'm not thrilled with
this idea, myself; I don't think the smaller conceptual footprint is enough to
outweigh the additional complexity in parameter handling. But its certainly a
viable option.
The second variation is probably the most conventional. There is nothing wrong
with it, and it would be easy to implement. But I find the third one more
interesting, and perhaps slightly cleaner to use. So that is what I'll present
as my completed API:
> var createMixin = (function() {
> var extend = function(destination, source) {
> for (var name in source) {if (source.hasOwnProperty(name)) {
> destination[name] = source[name];
> }}
> return destination;
> }
> var createMixinApi = function(api, initFn) {
> var name, result = {};
> var createRealApi = function() {
> var method = Array.prototype.shift.call(arguments);
> initFn.call(this);
> for (name in api) {if (api.hasOwnProperty(name)){
> this[name] = api[name];
> }}
> return this[method].apply(this, arguments);
> };
> for (name in api) {if (api.hasOwnProperty(name)) {
> result[name] = (function(name) {
> return function() {
> Array.prototype.unshift.call(arguments, name);
> return createRealApi.apply(this, arguments);
> }
> }(name));
> }}
> return result;
> };
> var create = function(api, initFn) {
> var mixin = createMixinApi(api, initFn);
> return function(destination) {
> return extend(destination, mixin);
> };
> };
> var createConstructorMixin = function(ctor) {
> return create(ctor.prototype, ctor);
> }
> createConstructorMixin.forApi = create;
> return createConstructorMixin;
> }());
Advantages
----------
### Simplicity ###
The biggest advantage to this mixin strategy that I see is just how clean it is.
You can create a mixin out of any API in a single line of code, and mix it into
any other API with just one more. Neither the source nor the destination needs
to know anything about the other.
### Flexibility ###
But it is also flexible. Imagine an API that looks like this:
> // creates Logger instances that can store and display log messages
> var Logger = function() {
> // initialize various listeners, do other setup
> };
> Logger.prototype = {
> debug: function(msg) { /* ... */ }, // default: ignored
> info: function(msg) { /* ... */ }, // default: console.info
> warn: function(msg) { /* ... */ }, // default: console.warn
> error: function(msg) { /* ... */ }, // default: console.error
> addListener: function(msgTypes, newListener) { /* ... */ },
> getMessages: function(type) { /* ... */ },
> enable: function(type) { /* ... */ },
> disable: function(type) { /* ... */ },
> };
If we would like to create a mixin out of this so that other objects could use
its implementation of `info`, `warn`, and `error`, but did not want the entire
Logger API mixed in, we could use the our mixin like this:
> Logger.mixinTo = createMixin.forApi({
> info: Logger.prototype.info,
> warn: Logger.prototype.warn,
> error: Logger.prototype.error
> }, Logger);
>
> // ... later ...
>
> Logger.mixinTo(MyObject.prototype);
If we also wanted the `getMessages` functionality, but were concerned that this
name was too generic for our mixin, we could simply alias it as
`getLogMessages`:
> Logger.mixinTo = createMixin.forApi({
> info: Logger.prototype.info,
> warn: Logger.prototype.warn,
> error: Logger.prototype.error,
> getLogMessages: Logger.prototype.getMessages
> }, Logger);
Or if our Logger constructor created a number of instance properties that were
unnecessary for our mixin case, we could instead use a custom function as the
initializer. This would require some knowledge of how the `Logger` constructor
worked. Let's imagine, for instance, that if we don't need the `addListener`,
`enable`, or `disable` properties of Logger, and that therefore the only
initialization required is to add the single property, `messageStore`, to our
instance. Then we can supply a simple function like this:
> Logger.mixinTo = createMixin.forApi({
> info: Logger.prototype.info,
> warn: Logger.prototype.warn,
> error: Logger.prototype.error
> }, function() {
> this.messageStore = [];
> });
But perhaps we really want to expose a partial API delegating all calls to an
instance created by the underlying constructor. We could do the following as a
one-off:
> Logger.mixinTo = createMixin.forApi({
> info: function() {return this.logger.info.apply(this.logger, arguments);},
> warn: function() {return this.logger.warn.apply(this.logger, arguments);},
> error: function() {return this.logger.error.apply(this.logger, arguments);}
> }, function() {
> this.logger = new Logger();;
> });
Or if we thought this was common, we could add to our function something that
would allow us to do:
> Logger.mixinTo = createMixin.delegatedApi(["info", "warn", "error"], Logger);
>
> // ... and then later ...
>
> Logger.mixinTo(MyObject.prototype, "logger"); // second param is delegate name
>
> // ...
>
> var obj1 = new MyObject(/* params */);
> // next line resolves to obj1.logger.warn("Problem found!");
> obj1.warn("Problem found!");
We'll leave details of how to write that as an excersize for the reader.
Also note that although we've mostly discussed mixing into the prototypes of
constructor functions, in fact we can mix in to any object at all.
> var obj1 = new MyObject(/* params */);
> Logger.mixinTo(obj1);
>
> var obj2 = {
> method1: function() {/* ... */},
> method2: function() {/* ... */}
> };
> Logger.mixinTo(obj2);
Disadvantages
-------------
### Memory Usage ###
One issue that might be significant is that this style takes more memory than a
pure constructor function approach. Each instance gets its own references to
each mixed in API function. Or at least it gets them when the API is actually
used; until then it simply inherits the wrapper functions attached to its
constructor. Note though that it does not get its own copy of the **function**,
only a copy of the **reference to that function**. So the memory overhead is not
huge, but it's real, and there is no way to eliminate it.
### Running the Prototype Functions Wreaks Havoc ###
Since in Javascript prototypes are simply plain objects, any of these wrapped
functions could be called as methods of the prototype. No one should ever do
something like this, of course, but if it did happen, it would cause significant
problems. The initializer would run on the prototype, adding whatever properties
it expected to add on an instance. The prototype wrapper functions would be
replaced by the underlying API. All future instances created from that prototype
would share a single state as would all instances that have not yet had their
own wrapper functions replaced. This would be bad<sup>TM</sup>.
But that is not a problem specific to this technique. It would happen for any
lazily instantiated instance properties created by functions on a prototype:
> Person.prototype.addFriend = function(friend) {
> if (!this.friends) {
> this.friends = [];
> }
> this.friends.push(friend);
> };
If `addFriend` was called on the prototype, then all `Person` instances that
have not yet had their own list of friends initialized (including all new Person
objects) would share a single list of friends. So, while this is a concern, it
seems to be more a case of "Doctor, it hurts when I do this. So don't do that."
And Th-th-th-that's All, Folks
------------------------------
This seems to be a useful technique, and it shows off some of the wonderful
dynamic nature of Javascript.
Please feel free to look at [the complete source][source] or the
[unit tests][tests], and [let me know][mailme] about any suggestions.
[source]: http://not.yet.ready/
[unit tests]: http://not.yet.ready/
[mailme]: mailto:scott.sauyet@gmail.com
#### Created May 3, 2012. Last updated May 3, 2012 ###