Skip to content
Lev Brie edited this page Jan 19, 2016 · 3 revisions

◄ Back (Objects)     Next (Inheritance) ►

The Module Pattern

The descriptions of this and other patterns comes from Addy Osmani's Learning JavaScript Design Patterns, available online for free.

You've already seen one way to define a module - using object literal notation:

var myEasyModule = {
  moduleId: '86492d69-2b86-4098-9739-2a96917a14e4',
  identify: function() {
    return this.moduleId;
  },
  config: {
    useCaching: true,
    language: 'en'
  },
  getCurrentConfig: function () {
    var description = (this.config.useCaching) ? 'enabled' : 'disabled';
    console.log('Caching is ' + description);
    if (this.config.language === 'en') {
      console.log('Language is ' + 'english');
    } else {
      console.log('Language is ' + this.config.language);
    }
  },
  disableCaching: function () {
    this.config.useCaching = false;
  }
};

myEasyModule.identify();
myEasyModule.getCurrentConfig();
myEasyModule.disableCaching();
myEasyModule.getCurrentConfig();

This object can function like a basic module, helping us to encapsulate and organize our code. But in order to further emulate class-like functionality in JavaScript, we can use a module pattern designed to include public and private methods and variables inside a single object, shielding these methods and variables from the global or from an enclosign scope. The module pattern encapsulates private state and organization using closures, and it works by returning a public API, usually as an object.

var myImmediatelyInvokedCounterModule = (function () {
  var counter = 0;

  return {
    incrementCounter: function () { return ++counter; },
    resetCounter: function () {
      console.log('Counter value prior to reset: ' + counter);
      counter = 0;
    }
  };
})();

myImmediatelyInvokedCounterModule.incrementCounter();
myImmediatelyInvokedCounterModule.resetCounter();     // reset from 1
myImmediatelyInvokedCounterModule.incrementCounter();
myImmediatelyInvokedCounterModule.incrementCounter();
myImmediatelyInvokedCounterModule.incrementCounter();
myImmediatelyInvokedCounterModule.resetCounter();     // reset from 3

The Revealing Module Pattern

The version of the module pattern that you will see most often, and that I recommend you use, is the revealing module pattern.

var myRevealingModule = (function () {

  var privateVar = "Ben Cherry",
      publicVar  = "Hey there!";

  function privateFunction() {
    console.log( "Name:" + privateVar );
  }

  function publicSetName( strName ) {
    privateVar = strName;
  }

  function publicGetName() {
    privateFunction();
  }

  // Reveal public pointers to
  // private functions and properties

  return {
    setName: publicSetName,
    greeting: publicVar,
    getName: publicGetName
  };

})();

myRevealingModule.setName( "Paul Kinlan" );

I highly recommend using this pattern to encapsulate functionality and keep your code organized and maintainable. At the same time, I also highly recommend that you do not use it to set public and private variables in quite the way that you would in a language like JavaScript. When you create private variables and methods inside of a module, they become almost impossible to test, and the module as a whole becomes harder to reason about. Instead, I prefer to create smaller modules that expose nearly all of their functionality, and which are therefore quite easy to test, but which supply a special convention for identifying private variables and methods (for example prefixing private methods with the $):

var shoppingCart = (function () {
  // private variables go up here
  var basket = [],
      tax = 0;

  // Return an object exposed to the public
  // I do this right away so that it's easier for another developer
  // to come look at this code and understand what it's doing
  return {
    addItem: addItem,
    getItemCount: getItemCount,
    getTotal: getTotal,
    addTax: addTax,
    outputBasket: outputBasket
  };

  // Add items to our basket
  function addItem(values) {
    basket.push(values);
  }

  // Get the count of items in the basket
  function getItemCount() {
    return basket.length;
  }

  // Get the total value of items in the basket
  function getTotal() {
    var index = this.getItemCount(),
        total = 0;

    while (index--) { total += basket[index].price; }
    return total;
  }
})();

◄ Back (Objects)     Next (Inheritance) ►