A basic introduction to the Mixin Pattern in JavaScript

Your Friendly Neighborhood Mixin Pattern

In past drips, we've talked about a couple of ways to implement inheritance in JavaScript. And inheritance can be a great way to reuse code if your new concept can be clearly expressed as a subtype—like a SuperHero is a SuperHuman. However, sometimes we need to take a different approach.

var spider = {
    senseDanger: function () {
        console.log("My spidey sense is tingling.");
    },
    climbWalls: function () {
        console.log("Can't keep me down.");
    },
    eatFlies: function () {
        console.log("Tasty!");
    }
};

var peterParker = Object.create(spider);
peterParker.name = "Peter Parker";

At first glance, it seems that having peterParker inherit from spider should work.

peterParker.senseDanger();
// => "My spidey sense is tingling."

peterParker.climbWalls();
// => "My spidey sense is tingling."

So far so good.

peterParker.eatFlies();
// => "Tasty!"

Hmm. That's not quite what we intended. Peter Parker may have spider powers, but he isn't actually a spider, or a subtype of spider.

What we need is a way to share common functionality between different object types without subordinating one object type to another. Maybe we could just do something like this?

var peterParker = { name: "Peter Parker" };

peterParker.senseDanger = spider.senseDanger;
peterParker.climbWalls = spider.climbWalls;

peterParker.senseDanger();
// => "My spidey sense is tingling."

peterParker.climbWalls();
// => "My spidey sense is tingling."

peterParker.eatFlies();
// => peterParker.eatFlies is not a function

This works exactly how we want. And in fact, this is a basic implementation of the mixin pattern. Essentially, the mixin pattern is the process of "mixing in" the behavior from one object into another.

But we can implement the mixin pattern in a much more elegant way using a simple utility function.

function mixin (target, source, names) {
    names.forEach(function(name) {
        target[name] = source[name];
    });

    return target;
}

var peterParker = { name: "Peter Parker" };
mixin(peterParker, spider, ["senseDanger", "climbWalls"]);

This mixin function takes a target object, a source object, and an array of property names to mixin from source to target, which nicely solves our problem.

One thing to keep in mind about this approach, though, is how your program makes use of the mixed-in methods. For instance, what if you need to share these spider powers across several other objects?

var peterParker = { name: "Peter Parker" };
mixin(peterParker, spider, ["senseDanger", "climbWalls"]);

var milesMorales = { name: "Miles Morales" };
mixin(milesMorales, spider, ["senseDanger", "climbWalls"]);

var miguelOhara = { name: "Miguel O'Hara"};
mixin(miguelOhara, spider, ["senseDanger", "climbWalls"]);

One approach that we could take is actually extracting the methods into an object specifically intended for mixing-in. To do that, we'll need to tweak our mixin function a bit.

function mixin (target, source, names) {
    names = names || Object.keys(source);

    names.forEach(function(name) {
        target[name] = source[name];
    });

    return target;
}

Now if we don't explicitly supply a names array, the mixin function will assume we want to mixin every property of the source object, which means we can refactor our code into something like this:

var spiderPowers = {
    senseDanger: function () {
        console.log("My spidey sense is tingling.");
    },
    climbWalls: function () {
        console.log("Can't keep me down.");
    }
};

var spider = {
    eatFlies: function () {
        console.log("Tasty!");
    }
};

mixin(spider, spiderPowers);

var peterParker = { name: "Peter Parker" };
mixin(peterParker, spiderPowers);

var milesMorales = { name: "Miles Morales" };
mixin(milesMorales, spiderPowers);

var miguelOhara = { name: "Miguel O'Hara"};
mixin(miguelOhara, spiderPowers);

Though it may not reflect the "real world" as well, this approach of extracting common role/behavior-based logic into a dedicated source object tends to result in cleaner code.

Fortunately, mixin helper functions are common in JavaScript libraries, so you don't need to maintain your own. You can find some in Underscore and jQuery under the name extend, and in Lo-Dash under the name assign.

Thanks for reading. Now go try mixing this pattern into your own projects!

Josh Clanton


Copyright © 2014 Design Pepper, All rights reserved.


Email Marketing Powered by Mailchimp