JavaScript Module Pattern: 2 Forms


JavaScript Module Pattern: 2 Forms

The module pattern is a common JavaScript coding pattern. It's generally well understood, but there are a number of advanced uses that have not gotten a lot of attention. In this article, I'll review the basics and cover some truly remarkable advanced topics, including one which I think is original.
In the following guide I will explain two different variation of module pattern that I use during development, namely:

  • Loose Augmentation
  • Function Import


Augmentation
One limitation of the module pattern so far is that the entire module must be in one file. Anyone who has worked in a large code-base understands the value of splitting among multiple files. Luckily, we have a nice solution to augment modules. First, we import the module, then we add properties, then we export it. Here's an example, augmenting our MODULAR from above:

// global object being added to the user space

var modular = (function (module) {
       
    module.filter = function (array, func) {
       
        var len = array.length;
        if (!isFunction("function"))
            throw new TypeError();
       
        // array to store result set
        var result = [];
        for (var i = 0; i < len; i++) {
            var curVal = array[i];
            if(func(curVal))
                result.push(curVal);
        }
        return result;
    };

    return module;
   
   
}(modular));


We use the var keyword again for consistency, even though it's not necessary. After this code has run, our module will have gained a new public method named modular.filter. This augmentation file will also maintain its own private internal state and imports.

Loose Augmentation
The above requires our initial module creation to be first, and the augmentation to happen second, that isn't always necessary. We can create flexible multi-part modules that can load themselves in any order with loose augmentation. Each file should have the following structure:
// global object being added to the user space

var modular = (function (module) {
   
    // private methods
    var isFunction = function(func) {
        return (typeof func != "function") 
    };

    // public method
    module.isEven = function(val) {
        return val % 2 == 0;
    };

    module.increment = function(val) {
        return ++val;
    };
   
    module.filter = function (array, func) {
       
        var len = array.length;
        if (!isFunction("function"))
            throw new TypeError();
       
        // array to store result set
        var result = [];
        for (var i = 0; i < len; i++) {
            var curVal = array[i];
            if(func(curVal))
                result.push(curVal);
        }
        return result;
    };

    return module;
   
   
}(modular || {}));




Tight Augmentation
The difference between loose and tight augmentation is the ability to override method.
The only change that is need to the above code is to change the closure input
(modular || {})); to (modular));


Private State
Any method that was declared without the module prefix will be considered private methods and can only be used within the current class. These might be methods specific to a specific module or implementation that only need to be used by that module. NB: Nothing in JavaScript is truly private
 For example the private isFunction might be used in a different way in a different class and might need to be implemented differently. A better example would be a currency translator for different countries. A convertor may be different for each country (Jamaica vs USA vs Trinidad), but the idea of currency convertor will be present in all modules.
var modular = (function (module) {
   
    // private methods
    var isFunction = function(func) {
        return (typeof func != "function") 
    };

    module.filter = function (array, func) {
       
        var len = array.length;
        if (!isFunction("function"))
            throw new TypeError();
       
        // array to store result set
        var result = [];
        for (var i = 0; i < len; i++) {
            var curVal = array[i];
            if(func(curVal))
                result.push(curVal);
        }
        return result;
    };

    return module;
   
   
}(modular || {}));



Function Import or Object Literal Augmentation
This mechanism is much different from the one shown above in that the functions are appended to an object literal directly and can be accessed across files. The beauty of Object Literal Augmentation (OLA) is that the global space does not have to be clobbered with functions, but everything can be attached to a single object literal while offering cross-file access and the use of the this keyword. J
An example is show below: Strings.js
// global object being added to the user space
var modular = modular || {}

modular.filter = function (array, func) {
   
    var len = array.length;
    if (!this.isFunction("function"))
        throw new TypeError();
   
    // array to store result set
    var result = [];
    for (var i = 0; i < len; i++) {
        var curVal = array[i];
        if(func(curVal))
            result.push(curVal);
    }
    return result;
};

modular.isFunction = function(func) {
    return (typeof func != "function") 
};

modular.isEven = function(val) {
    return val % 2 == 0;
}

modular.increment = function(val) {
    return ++val;
}


In the above file I initialized the object literal / import the object modular if it already exists. Notice that all the methods are added to the modular object, which means that the values are prepended to a single object reducing global clutter.
StringsAdd.js
// global object being added to the user space
var modular = modular || {}

modular.map = function (array, func) {
   
    var len = array.length;
    if (!this.isFunction("function"))
        throw new TypeError();
   
    // array to store result set
    var result = [];
    for (var i = 0; i < len; i++) {
        var curVal = array[i];
            result.push(func(curVal));
    }
    return result;
};

 With-in the other file / sub module the importation is done. It will get the modular object that already exist in the global space and assign it to modular. Then additions can be done is shown above.
All the objects from the 2orX plus files will be appended to a single object which can be used and methods can be shared with-in the object. The benefit of this implementation is that it scales well linearly; thousands of methods can be appended to a single object.






Comments

Post a Comment

Popular posts from this blog

Pseudo-Random UUID Generation with mask support

Mocking Ajax with the JQuery Mockjax Library