Javascript Application Design Patterns, By Example

Share this article

Javascript development is dominated by libraries libraries like Backbone, Spine, and of course jQuery (surprise!). However, it isn’t an issue of what library to use but rather how to use it.

Understanding how some frameworks provide scaffolding and architecture to a seemingly non-deterministic glob of javascript can be a key component in our arsenal of javascript tricks. Having this knowledge essentially unlocks the last door to creating truly great and immersive web applications by letting the developer logically separate concerns and dependencies.

In this article, we will walk through some basic javascript patterns:

  • IIFE Modules
  • Dojo Modules
  • jQuery Modules
  • AMD Modules
  • CommonJS Modules
  • Facade Patterns
  • Mediator Modules

Module patterns — Immediately Invoked Expression Functions (IIEF) use execution context to create privacy.

var module = (function(){
    /**
     * private variables are declared only inside the module
     */
    var basket = [];

    /**
     * public variables are declared in the returned object
     */
    return {
        add: function(value){ ... },
        count: function() { ... }
    };
}());

module.add('a');
module.add('b');
module.add('c');

var total = module.count();

Module Pattern – Dojo

/**
 * traditional method
 */
var store = window.store || {};
store.basket = store.basket || {};

/**
 * dojo method
 */
dojo.setObject("store.basket.object", function(){
    var basket = [];
    function privateFunc(){ ... }
    return {
        add: function(value){ ... },
        count: function(){ ... }
    }
});

Module Pattern – jQuery

function library(module) {
    $(function(){
        if (module.init) {
            module.init();
        }
    });
    return module;
}

var myLibrary = library(
    function(){
        return {
            init: function() {
                /*implementation*/
            }
        };
    }()
);

Better – Asynchronous Module Definition, or AMD

/**
 * AMD: define()
 * define a signature with define(id /*optional*/, [dependencies], /*factory module instantiation of fn*/);
 */

define(
    /*module id*/
    'myModule',

    /*dependencies*/
    ['foo', 'bar;, 'baz'],

    /*definition for the module export*/
    function(foo, bar, baz){

        /*module object*/
        var module = {};

        /*module methods go here*/
        module.hello = foo.getSomething();
        module.goodbye = bar.getSomething();

        /*return the defined module object*/
        return module;
    }
);

/**
 * AMD: require()
 * load top-level code for JS files or inside modules for dynamically fetching dependencies
 */

/* top-level: the module exports (one, two) are passed as function arguments ot the object */
require(['one', 'two'], function(one, two){
    ...
});

/*inside: the complete example*/
define('three', ['one', 'two'], function(one, two){
    /**
     * require('string') can be used inside the function to get the module export
     * of a module that has already been fetched and evaluated
     */

    var temp = require('one');

    /*this will fail*/
    var four = require('four');

    /* return a value to define the module export */
    return function(){ ... };
});

Best: CommonJS – Widely adopted server-side format

/**
 * basically contains two parts: an exports object that contains the objects a module wishes to expose
 * and a require function that modules can use to import the exports of other modules
 */

/* here we achieve compatibility with AMD and CommonJS using some boilerplate around the CommonJS module format*/
(function(define){
    define(function(require,exports){
         /*module contents*/
         var dep1 = require("foo");
         var dep2 = require("bar");
         exports.hello = function(){...};
         exports.world = function(){...};
    });
})( typeof define=="function" ? define : function(factory){ factory(require, exports) });

** Harmonious revelations: ES Harmony, the the successor to ECMAScript 5

/**
 * 1. static scoping
 * 2. simple
 * 3. reusable modules
 */

// Basic module
module SafeWidget {
    import alert from Widget;
    var _private ="someValue";

    // exports
    export var document = {
        write: function(txt) {
            alert('Out of luck, buck');
        },
        ...
    };
}

// Remote module
module JSONTest from 'http://json.org/modules/json2.js';

Alternative patterns to modules

Modules are regularly used in MVC applications..but there are other patterns that can make building large apps easier too.Remember that jQuery generally plays a smaller role in larger-apps than most people might think. The 2nd.MD calendar and booking code, and the chat portal, could easily operate without jQuery.

Facade – high level interfaces to large bodies of code that hide MOST of the unerlying complexity

“When you put up a facade, you’re usually creating an outward appearance which conceals a different reality. Think of it as simplifying the API presented to other developers”

Essential Javascript Design Patterns

  1. Simplifies usage through a limited, simpler API
  2. Hides the inner-workings of the library, allows implementation to be less important
  3. Lets you be more creative behind the scenes
  4. Has many functional behaviors for application security
var module = (function() {
    var _private = {
        i:5,
        get : function() {
            console.log('current value:' + this.i);
        },
        set : function( val ) {
            this.i = val;
        },
        run : function() {
            console.log('running');
        },
        jump: function(){
            console.log('jumping');
        }
    };

    /**
     * this part includes code imported above and provides an API to the returning module
     */
    return {
        facade : function( args ) {
            _private.set(args.val);
            _private.get();
            if ( args.run ) {
                _private.run();
            }
        }
    };
}());

module.facade({run: true, val:10}); //outputs current value: 10, running

Mediator – Promotes loose coupling by establishing interfaces for events that modules can subscribe to:

  1. Allows modules to broadcast or listen for notifications without worrying about the system or tedious callback chains.
  2. Notifications can be handled asynchronously by any number of modules at a single time.
  3. Much easier to add or remove features at any time due to the loosely coupled nature of the code.
var mediator = (function(){
    var subscribe = function(channel, fn){
        if (!mediator.channels)mediator.channels = [];
        mediator.channels.push({ context: this, callback:fn });
        return this;
    },
    publish = function(channel){
        if (!mediator.channels) return false;
        var args = Array.prototype.slice.call(arguments, 1);
        for (var i = 0, l = mediator.channels.length; i<l; i++) {
            var subscription = mediator.channels[i];
            subscription.callback.apply(subscription.context,args);
        }
        return this;
    };
    return {
        channels: {},
        publish: publish,
        subscribe: subscribe,
        installTo: function(obj){
            obj.subscribe = subscribe;
            obj.publish = publish;
        }
    };
}());

That is all for now! Remember that how you organize your code and architect your application can really simplify complex programs to an almost natural instinct. Hone your skills on these approaches and you will master techniques needed to really grow as a developer!

Sam DeeringSam Deering
View Author

Sam Deering has 15+ years of programming and website development experience. He was a website consultant at Console, ABC News, Flight Centre, Sapient Nitro, and the QLD Government and runs a tech blog with over 1 million views per month. Currently, Sam is the Founder of Crypto News, Australia.

backbonejQuerypatterns
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week