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 ###