DRY JavaScript with mixins

CREDITS: Many thanks to Ryan Niemeyer for his review and feedback on this article.

One of the more valuable JavaScript concepts I learned after starting work with the awesome folks at appendTo last year was the idea of using mixins for common functionality that you want to share across multiple objects/modules in your code. It was so valuable in fact that I'm a little embarassed that it has taken me this long to share the idea (not that it's a new concept in general), because I've been surprised at the relatively few number of people I've encountered since that were aware of the technique. So I decided that I needed to write about it.

What is a mixin?

Wikipedia defines a mixin as:

In object-oriented programming languages, a mixin is a class which contains a combination of methods from other classes. How such combination is done depends on the language, but it is not by inheritance. ...A mixin can also be viewed as an interface with implemented methods. When a class includes a mixin, the class implements the interface and includes, rather than inherits, all the mixin's attributes (fields, properties) and methods.

This is helpful for understanding the intent of a mixin, but the application is slightly different in the context of JavaScript of course, it being a dynamically typed, interpreted language. In this context, mixins allow the developer to dynamically add functionality, defined in a single place (this is where the DRY part comes in), to other modules within your code.

This is probably most effectively understood with a good example...

Something simple

I'll start with a very basic example, and then we'll take a look at something a bit more practical to help illustrate how mixins work.

Let's say you want to be able to log messages to the browser console, but only if/when the console object is actually available (so your app doesn't throw JS errors on older browsers that don't expose a console object), but you don't want to have a bunch of if checks scattered everywhere you want to write stuff to the console in your code. So let's create a simple mixin with a single log function.

(function () {
    var Logger = {
        log: function (message) {
            if (window.console && typeof console.log === "function") {
                console.log(message);
            }
        }
    };

    return Logger;
}());

Now let's create an object that will need to be able to log stuff to the console, and use our the log function from our mixin to handle it:

(function (_, Logger) {
    var MyObject = function () {
        this.doSomething = function () {
            this.log("My Object is doing something!");
        };
    };

    // This copies the members of the `Logger` object onto the prototype of `MyObject`
    _.extend(MyObject.prototype, Logger);

    return MyObject;
}(underscore, Logger));

Now, when we create a new instance of MyObject:

var obj = new MyObject();  

This instance has a function on it called log that was copied on to it from our mixin, which we can call from inside the doSomething function:

obj.doSomething();  
// "My Object is doing something!"

Hopefully that makes sense...now let's take a look at something a bit more practical...

A reusable AJAX call

One area that a lot of code-bases that I've worked with could benefit from some reusable code is with AJAX calls. I've seen a LOT of applications that have something very much like this, sprinkled around in dozens of places in their code (THIS is what we want to fix):

$.ajax({
    url: "http://example.com/foo",
    type: "get",
    contentType: "application/json; charset=utf-8"
})
.done(function (data) {
    // ...
})
.fail(function () {
    // ...
});

Lets take a look at how we can fix that by creating a mixin that we can use in all of our objects that need to be able to make AJAX calls to the server.

Define it in one place

So, let's create our mixin: an object that has a function to take the appropriate parameters and executes an AJAX call:

(function (_, $) {

    var Requester = {
        _mapRequestData: function (request) {
            var mappedRequest = {};

            _.each(request, function (value, key) {
                mappedRequest[key] = this._executeOptions[key](value, this);
            }, this);

            return mappedRequest;
        },

        _executeOptions: {
            url: function (target, context) {
                return target;
            },

            type: function (target, context) {
                return target;
            },

            contentType: function (target, context) {
                return target;
            },

            context: function (target, context) {
                return target;
            },

            data: function (target, context) {
                if (context[target]) {
                    return context[target];
                } else {
                    return target;
                }
            },

            dataType: function (target, context) {
                return target;
            },

            done: function (target, context) {
                return context[target];
            },

            fail: function (target, context) {
                return context[target];
            }
        },

        execute: function (options) {
            var self = this;

            if (!options && !options.url) {
                return;
            }

            options = this._mapRequestData(options);

            var request = $.ajax({
                url: options.url,
                type: options.type || "get",
                contentType: options.contentType || "application/json; charset=utf-8",
                data: options.data || {},
                context: options.context || self
            });

            if (options.done) {
                request.done(options.done.bind(options.context || self));
            }

            if (options.fail) {
                request.done(options.fail.bind(options.context || self));
            }

            if (options.always) {
                request.done(options.always.bind(options.context || self));
            }

            return request;
        }
    };

    return Requester;

}(underscore, jquery));

OK, so there we have a nice, self-contained module that returns an object literal called Requester that we can now mixin to any other module in which we need to make AJAX calls. So now lets take a look at how we would do that.

Dependencies and inner-workings

Before we get into that, real quick, lets talk about dependencies, and the inner-workings of how this works...

You could do this yourself, but I've never been a big fan of reinventing wheels. I typically try to take advantage of the hard work of others when they've been so generous as to share that work with us. There are basically two libraries that I use to handle mixin functionality, and this has never really been a problem for me, because I honestly can't remember a project that I've worked on that didn't have at least one of these libraries (if not both) available: jQuery and underscore.js. Both of these libraries provide an extend function that provides, effectively, the same functionality, though the syntax between the two differs slightly, so I'll provide examples of how to use this mixin with each of those functions.

What the extend function is going to do for us, is copy all of the functionality from a source object, to a destination object, attributes, functions, etc.

The underscore.js documentation describes it like this:

_.extend(destination, *sources)  

Copy all of the properties in the source objects over to the destination object, and return the destination object. It's in-order, so the last source will override properties of the same name in previous arguments.

And the jQuery documentation is similar:

jQuery.extend( target [, object1 ] [, objectN ] )  

Description: Merge the contents of two or more objects together into the first object.

The difference between the functionality of the two should be more obvious when you see the difference in usage syntax below.

OK, so lets take a look at how we would use these functions to pull our Requester mixin into another object/module.

Mixin it up

Lets say we have a view object that will be responsible for updating DOM elements with the result of an AJAX call to the server. Here's how we would define that view, using the Requester mixin we've already defined:

(function (_, $, Requester) {

    // constructor function
    var SomeView = function () {};

    _.extend(SomeView.prototype, {
        requests: {
            getSomeData: {
                url: "http://example.com/foo",
            }
        },

        getSomeData: function () {
            // this `execute` function is mixed-in from our `Requester`
            this.execute(this.requests.getSomeData)
                .done(this.onGetSomeDatadone)
                .fail(this.onGetSomeDataFail);
        },

        onGetSomeDataDone: function (data) {
            // Update the appropriate DOM elements with the returned `data`
        },

        onGetSomeDataFail: function (err) {
            // Something went wrong...do something to indicate so to the user
        }
    });

    // extend SomeView with our Requester mixin
    _.extend(SomeView.prototype, Requester);

    return SomeView;

}(underscore, jquery, Requester));

OK, so, lets take a little closer look at what's going on here...

First of all, notice how/where we're mixing in our Requester:

_.extend(SomeView.prototype, Requester);  

Here's how you would do the same thing with jQuery instead:

$.extend({}, SomeView.prototype, Requester);

Recall what the documentation said about the extend function. This call is literally adding the execute function we defined in Requester to the SomeView object. So when we create an instance of SomeView, it has the execute function on it, just as if it were originally defined within SomeView itself.

Also notice that the execute function accepts an object that it uses in its execution of the $.ajax call. This gives us a nice, clean way to pre-define the options we want to use for that call on the requests property of our object, just as we did in SomeView:

requests: {  
    getSomeData: {
        url: "http://example.com/foo"
    }
}

With all of that in place, now all we have to do is pass the getSomeData request definition in to the execute function, and our request will be submitted to the server.

this.execute(this.requests.getSomeData);  

Also notice that the execute function returns something. As of version 1.5, jQuery now returns a Deferred object from the execution of the $.ajax function. The Deferred is an implementation of the Promise pattern and provides a chainable object that allows us to attach callback functions to the execution of our $.ajax request. This allows us to handle the results of the request in a clean, elegant and readable manner:

this.execute(this.requests.getSomeData)  
    .done(this.onGetSomeDataDone)
    .fail(this.onGetSomeDataFail);

This gets the repetitive, boilerplate code out of the way, and still clearly shows the intent of the code.

The benefits

You might be asking yourself why this is such a big deal, so in case you are struggling to see the advantages of this approach to reusable JavaScript code, allow me to point out some of them here for you.

Reuse, elimination of boilerplate code

Notice that we no longer need to explicitly write out the repetitive calls to the $.ajax function. It's now defined in one place, and we've effectively encapsulated the parts that vary as we should be doing. Now all we have to do is mix Requester in to our object, and call this.execute with the appropriate request definition, and we're done.

Flexibility and default values

You might've noticed that in our mixin, inside the execute the function, when we call $.ajax, when we're passing our options to it, note how we're doing so:

$.ajax({
    url: options.url,
    type: options.type || "get",
    data: options.data || {},
    dataType: options.dataType || "json",
    contentType: options.contentType || "application/json; charset=utf-8"
});

Take a look at the contentType option for example. Notice that we're saying: "use the contentType that's defined on the options passed in if it's defined, but if not, we'll go ahead and use application/json; charset=utf-8 by default if it's not defined."

Now, notice how this allowed us to define our getSomeData request with only a URL:

getSomeData: {  
    url: "http://example.com/foo"
}

We can do this because our mixin is assuming that if we don't define a type on our request, it will automatically assume we're executing a GET request, with a dataType of json and a contentType of application/json; charset=utf-8.

This same functionality allows us to easily override any of these defaults when a request needs to. Say you had a request where you were POSTing form data with the request instead of a JSON object, and were expecting XML back from the server instead of JSON (heaven forbid...). You could define your request to override the defaults like this:

getSomeOtherData: {  
    url: "http://example.com/bar",
    type: "post",
    dataType: "xml",
    contentType: "application/x-www-form-urlencoded; charset=UTF-8"
}

Ultimately all of that is a significant win in our code.

Wrapping it up

After all of that, I hope you can see that the important takeaway from all of this is the high-level concept and benefits that JavaScript mixins can give you. It's not really about the AJAX, that's just the example use case. The possibilities are endless really. Any kind of code that would be common among many of the objects in your code would be a great use for a mixin. All of your objects need to be able to log messages? Create a logging mixin. Have a common messaging infrastructure that you use across your code? Wrap it up in a mixin and pull it in to the objects that need to use it.

Questions or comments? Know of a better way to do this? Let me know. Hopefully this was helpful to you.