Saturday, January 4, 2014

The JavaScript Module Pattern


It allows private members that can be used to maintain state using closure, and exposing only public members. So only a public API is exposed, keeping everything within the closure (nested function using the private variables) as private. This pattern is similar to an IIFE, save that an object is returned instead of result of a function execution.

Note that JavaScript don't have getters and setters or access modifiers, so we use closures and function scopes to simulate private members and maintain state inside a module.

//Shopping Cart Module
var shoppingCart = (function(){
      var cart= [];  //private
      
      //public API
      return {
            addItem: function(value) {
                    cart.push(value);
            },
            itemCount: function() {
                   return cart.length;
           },
           totalPrice: function() {
                    var i = this.itemCount(),
                               p = 0;

                    while(i--) {
                          p += cart[i].price;
                    }
                   return p;
           }
     }      
}());

The shopping cart module is self-contained in a global object called shoppingCart. The cart array is private and only exists within the module's closure. The addItem and itemCount methods can access the cart array as it is within the scope of the outer function.

The module returns an object that gets assigned to shoppingCart as it is immediately invoked. this way we can interact with the shoppingCart object as follows:

//call an API method of the module
shoppingCart.addItem({item:'Effective JavaScript',price:8.99});

//this will work
console.log(shoppingCart.itemCount());
console.log(shoppingCart.totalPrice());

//this will return undefined
console.log(shoppingCart.cart);

Notice that the methods defined in the shoppingCart module have been effectively namespaced with shoppingCart and "avoids" global collisions. Of course, the issue of global variable declaration still remains if we have another global variable called shoppingCart declared.

Also, an object literal can be used to define a module by just assigning it to a variable! This is sufficient if we don't need to maintain state or require private variables. Object literals cannot instantiate using the new operator and we can simply start using the object declared as an instance.

No comments:

Post a Comment