Mixins and Constructor Functions

JavaScript allows programmers to take properties from one object and mix them into another object. There are several ways to accomplish this mixing and a few of them are explored here.

Observable Mixin

Here is a simple example of the observer pattern that can be mixed into other objects.

var observableMethods = {
    observe: function(observer) {
        if (!this.hasOwnProperty('observers')) {
            this.observers = [];
        }
        this.observers.push(observer);
    },
    notify: function(data) {
        if (this.hasOwnProperty('observers')) {
            for (var i=0, ilen=this.observers.length; i<ilen; i++) {
                this.observers[i](data);
            }
        }
    }
};

It is possible to use the observableMethods function as a observable itself.

observableMethods.observe(function() {
    alert('hi');
});
observableMethods.notify();

A little mixin function to make other things observable.

function mixinObservable(sink) {
    for (var p in observableMethods) {
        if (observableMethods.hasOwnProperty(p) &&
            typeof observableMethods[p] === 'function') {
            sink[p] = observableMethods[p];
        }
    }
}

We can mixin to an person created with an object literal.

var person = {
    name: 'Steve',
    setName: function(name) {
        var oldName = this.name;
        this.name = name;
        this.notify({oldName: oldName, newName: this.name});
    }
};
mixinObservable(person);
person.observe(function(data) {
    alert(data.oldName + ' was renamed to ' + data.newName);
});
person.setName('Sarah');

Alternately we write a constructor function for observable people that we can rename.

function Person(name) {
    this.setName(name);
};

mixinObservable(Person.prototype);

Person.prototype.setName = function(name) {
    var oldName = this.name;
    this.name = name;
    this.notify({oldName:oldName, newName:this.name});
};

We can then make a person and manipulate it.

var person = new Person('Steve');
person.observe(function(data) {
    alert(data.oldName + ' was renamed to ' + data.newName);
});
person.setName('Sarah');

In all of the above code, the three uses of hasOwnProperty are critical to understand.

The first two uses of hasOwnProperty in observe and notify ensure that the object into which the methods have been mixed, will have its own set observers and not share some set of observers with any other object. The unfortunate part is that these checks run every time the observe and notify methods are called. This is inefficient and something we want to fix.

The third use of hasOwnProperty in mixinObservable means that only properties directly on observableMethods will be mixed into other objects. This is important because if we do not use hasOwnProperty then we copy all of the enumerable Object.prototype properties which is either wasteful or will overwrite any custom methods with the same names on other objects.

The mixinObservable function also copies only functions. If this check was not made and someone had observed the observableMethods object itself then the observableMethods.observes array would be copied as part of every mixin and observers would be shared by some observable objects (e.g. observableMethods and Person.prototype in the example above.)

A simpler mixinObservable method could be the following but this could be more difficult to maintain. If more methods are added to observableMethods then they need to be explicitly listed in mixinObservable.

function mixinObservable(sink) {
    sink.observe = observableMethods.observe;
    sink.notify = observableMethods.notify;
}

Fixing the Inefficiency

The inefficiency in observe and notify can be fixed by making a constructor function for Observable objects.

function Observable() {
    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);
    }
};
Observable.call(Observable.prototype); // optional depending what you want

Now the observe and notify methods of an observable function are more efficient as we know the observers property was created when the Observable constructor function ran.

The last line, Observable.call(Observable.prototype);, is a bit of an unusual one but it makes it possible to observe Observable.prototype just like observableMethods.

A new mixin function.

function mixinObservable(sink) {
    for (var p in Observable.prototype) {
        if (Observable.prototype.hasOwnProperty(p) &&
            typeof Observable.prototype[p] === 'function') {
            sink[p] = Observable.prototype[p];
        }
    }
    Observable.call(sink); // optional depending what you want
}

Mixing into a person created with an object literal just like it was done above. The last line of this new mixinObservable insures the observers property is created.

var person = {
    name: 'Steve',
    setName: function(name) {
        var oldName = this.name;
        this.name = name;
        this.notify({oldName: oldName, newName: this.name});
    }
};
mixinObservable(person);
person.observe(function(data) {
    alert(data.oldName + ' was renamed to ' + data.newName);
});
person.setName('Sarah');

The trick comes when we create a Person constructor function.

function Person(name) {
    Observable.call(this);
    this.setName(name);
};

mixinObservable(Person.prototype);

Person.prototype.setName = function(name) {
    var oldName = this.name;
    this.name = name;
    this.notify({oldName:oldName, newName:this.name});
};

The first line of the constructor function, Observable.call(this);, ensures that each Person object has its own set of observers. Without this call, all the people will share the same list of observers which is the set of observers on the Person.prototype object. If this makes you squint then it is well worth the effort to think about it until it is clear why.

To use some class vocabulary, the first line of the constructor function can be thought of as a super call and that the Person class inherits from the Observable class. Some JavaScript diehards cringe at the mention of this vocabulary but I think the comparison is worth consideration.

Multiple Mixins

Multiple mixins follow the same pattern.

function Person(name) {
    Observable.call(this);
    Common.call(this);
    this.setName(name);
}

mixinObservable(Person.prototype);
mixinCommon(Person.prototype);

// By coincidence mixinCommon also added a notify method which
// clobbered the method of the same name added by mixinObservable.
// Fix this problem making appropriate decisions about how
// to call both.
Person.prototype.notify = function() {
    Common.prototype.notify.call(this);
    Observable.prototype.notify.apply(this, arguments);
};
// ...

Prototype Chaining vs. Mixins

There is one primary difference between prototype chaining and mixins. For example,

function Person(name) {
    Observable.call(this);
    Base.call(this);
    this.setName(name);
}

// chain prototypes so that all Person objects
// inherit from Base.prototype.
Person.prototype = Object.create(Base.prototype);
Person.prototype.constructor = Person;
Base.call(Person.prototype); // optional depending what you want

mixinObservable(Person.prototype);

Now if we add methods to both Base.prototype and Observable.prototype after the above code has executed. Only the method added to Base.prototype will be added to Person objects.

Base.prototype.getId = function() {/*...*/};
Observable.prototype.getObservers = function() {/*...*/};

var person = new Person('Steve');
person.getId(); // ok
person.getObservers(); // error: getObservers not defined

Enjoy your mixins.

Comments

Have something to write? Comment on this article.

Angus Croll April 24, 2012

As usual, an excellent and enlightening article Peter.

I like the way you use 'call' to add observable methods to both Observable prototype (in one example) and each person instance (in another). Call and apply work brilliantly with dynamic this binding (one reason why I am worried about fat arrow syntax and the way its hard-lexical-binding will declaw call and apply). By the way - are you into 'this' suddenly? :-)

The two examples I cite illustrate the principal of my favorite mixin strategy - functional mixins.

The beauty of this approach is that you get complete, procedural control over the mixin process, by invoking the mixin source as a script, rather than copying. Now we can assign this.observers directly to the target object from within our mixin script so there is no need for hasOwnProperty checks and the mixed in functions become more nimble. You can also, optionally, pass arguments to customize the mixin script.

Of course there is extra overhead in invoking the mixed in functions during the definition process, but this is a one time initialization expense.

Here are your examples using my mixin strategy (also in JS fiddle here)

var observableMethods = function() {

   this.observers = [];

   this.observe = function(observer) {
       this.observers.push(observer);
   };

   this.notify = function(data) {
       for (var i=0, ilen=this.observers.length; i<ilen; i++) {
           this.observers[i](data);
       }
   }
};

var person = {
   name: 'Steve',
   setName: function(name) {
       var oldName = this.name;
       this.name = name;
       this.notify({oldName: oldName, newName: this.name});
   }
};

observableMethods.call(person);
person.observe(function(data) {
   alert(data.oldName + ' was renamed to ' + data.newName);
});

person.setName('Sarah');
Misha April 24, 2012

Hello Peter, thanks for another great read.

Could you expand on “optional depending what you want” comment? When would I want to observe the prototype?

Peter Michaux April 25, 2012

Misha,

Generally I’m fumbling around with the philosophical idea that the prototype object should be a complete, fully-formed object useful in its own right. I think of Plato and his idea that a prototype is the ideal object from which all other objects are cloned. The perfect horse.

I can imagine the Observable.prototype object being accessed directly and used as a pub/sub object. More observable objects can be created by the Observable constructor with the global pub/sub object as the prototype of each object produced.

This is contrary to the common JavaScript situation where the prototype is a partial object with some missing pieces. The horse with no legs. Only the objects inheriting from the prototype are complete.

Misha April 27, 2012

Wow, Peter, this is a very interesting angle to look at it. I was always thinking of and treating prototype like a piece of machinery.

Funnily, the wiki article on Plato’s idealism is referring to an “ideal” Tree with a capital, just like we do with the constructors :-)

Thanks for inspiration.

Scott Sauyet April 30, 2012

Although I like this approach, I’m bothered by something here, and it seems fairly fundamental. This technique requires you to both mix the Observable in with the Person prototype and to update the Person constructor so that the required list of observers is created for each instance. That feels wrong.

I really want to do something like this:

Observable.mixinTo(Person.prototype);

And have it handle both of those chores.

I was thinking about this problem last week, and I have tried another technique which seems to do that, at a cost of each Person having copies of the reference to each Observable method. (It doesn’t copy the functions, only the references.) It does this by creating a temporary API on the constructor with functions that will, when called on an instance, call the necessary initialization and then place references to the real API on the instance.

I wrote a generic function that can create mixins from an API and an initialization function:

Observable.mixinTo = createMixinFunction(Observable.prototype, Observable);

Here the API is simply the entire prototype, and the initialization is the constructor, but they can be pretty much arbitrary. This function would then be used like this:

Observable.mixinTo(Person.prototype);

And the Person constructor would not have to call Observable on the instance. That strikes me as much cleaner.

I’m planning to write this up as an article, but thought I’d respond here, since this is what spurred me to take what I’d scratched out on paper on a bus ride and test it. You can find a first draft of the function at http://scott.sauyet.com/Javascript/Test/Mixin/

Thanks for posting this interesting information.

kangax May 1, 2012

@Angus

I use the same approach as yours. Only in this case, I would share the functions instead of creating them every time mixin is invoked.

For example:

var observableMethods = function() {
    this.observers = [];
    this.observe = observableMethods.observe;
    this.notify = observableMethods.notify;
};

observableMethods.observe = function(observer) {
    this.observers.push(observer);
};

observableMethods.notify = function(data) {
    for (var i=0, ilen=this.observers.length; i<ilen; i++) {
        this.observers[i](data);
    }
};

Or, alternatively, using closure to store observe/notify.

Have something to write? Comment on this article.