Module & Revealing Module Pattern

In the constructor pattern, we create an object using constructor functions, but in the module pattern, we use the object literals. We already know the ways to create an object, if you’d like to read about it, click here.

Creating an object using object literals is a simple task. We need to provide the key-value pairs, and that is it. The value can be anything, a primitive data type, an object, a function, an array, and so on. Here is an example:

var myObject = {
    config: {
        locale: "en-UK",
        timezone: "GMT"
    },
    getLocale: function () {
        return this.config.locale;
    },
    appName: "My App"
}

// accessing module's property
myObject.getLocale ();    // returns "en-UK"

Module Pattern

But, we should not use such objects as a module. The first thing to note here is everything is in the global scope. What if we wanted to keep a private variable, i.e. something which should not be accessible by everyone?

In JavaScript (ES5), the functions have the scope. So we need to wrap the object inside a function and, that should do the trick. To do this, we can use the IIFE (immediately invoked function expressions) so that we don’t need to execute the function explicitly. Here is the modified version of the same example, with a few additions.

var myObject = (function () {
    var _privateVar = "JS";
    var _privateFn = function () {};
    return {
        config: {
            locale: "en-UK",
            timezone: "GMT"
        },
        getLocale: function () {
            return _privateVar + " " + this.config.locale;
        },
        appName: "My App"
    }
})();

// accessing module's property
myObject.getLocale ();    // returns "JS en-UK"
myObject.privateVar;      // reference error
myObject.privateFn ();    // reference error

One limitation of creating an object using this method is, it gives only one instance. So if we want to create a contact widget, for example, (as discussed in this post), where we have to create multiple instances of the object, we’ll have to do that explicitly.

The immediately invoked function returns an object, using the create method of Object constructor we can create multiple instances, like this the following example. This way, it won’t be much different from the constructor pattern.

var myObject1 = Object.create (myObject);
var myObject2 = Object.create (myObject);

Revealing Module Pattern

What revealing module pattern tells is, instead of creating an object inside the IIFE, add everything as private properties. In the next step, return an object which contains the references to functions and variables which need to be made public. Here is an example.

var myObject = (function () {
    var _config = {
        locale: "en-UK",
        timezone: "GMT"
    };
    var _getLocale = function () {
        return config.locale;     
    }
    var _privateVar = "JS";
    var _privateFunction = function () {
        return privateVar;
    }

    return {
        getLocale: _getLocale,
        publicVar: _privateVar
    }
})();

Note: In JavaScript, there are no access modifiers like C++/Java, so it is not possible to create private variables by writing private keyword. We need to rely on function scopes to hide the variables from the global scope. For the reference, it is a coding standard to include an underscore as the first character of the variable to assume the variable is going to be used as a private variable.

While using any IIFE, we can make use of the function’s argument to pass references to the function’s scope. One common use of this to send the namespace variable or any library. For example:

var myModule = (function (App, $) {
    // myApp can be referenced in this entire scope using App
    // jQuery can be referenced using $
})(myApp, jQuery);

If the question is, which of this two, one should use, then the answer would be: whatever fits best. A design pattern is not a framework, but it is a way of writing code. A method on which a team can rely and agree. Still, there are scenarios or use cases where one pattern won’t fit, it would work, but it might not be the best choice.