Module pattern template

My current project makes use of different technologies, for presentation we rely on a mix of Java EE and JavaScript. We try not to mess around too much with the latter, and we strive to keep that code as readable as possible. For this reason we break the JavaScript code in modules that, ideally, are all written following the same template, so that it is easy for anyone in the team to put his hands on any piece of code available.

Here is an example of how we typically write a module.

As you will see, we use the well known module pattern, with a few specific variations:
var APP = APP || {}; // 1

APP.Module = (function(app, global, $) { // 2
  "use strict"; // 3

  var FIRM_NAME = 'XYZ'; // 4

  function Factory(options) { // 5
    var that = this; // 6

    if(!(this instanceof Factory)) { // 7
      return new Factory();
    }

    this.options = $.extend({ name: null }, options); // 8

    function _checkName() { // 9
      if(that.options.name) {
        console.log('In a private function, use that instead of this');
        return true;
      }
      else {
        return false;
      }
    }

    this.getName = function getName() { // 10
      return FIRM_NAME + (_checkName() ? ': ' + this.options.name : '');
    }

    return this; // 11
  }

  Factory.aFunction = function() { // 12
    if(this == Factory) {
      console.log('A static function has no access to instance variables');
    }
    else { // called by sayHello, see below
      console.log('Instance', this.options.name);
    }
  }

  Factory.prototype.sayHello = Factory.aFunction; // 13

  return Factory; // 14
}(APP, this, jQuery)); // 15
1. First thing, I define a namespace for all the modules. Or better, if APP is already defined, I use it as it is in the following code. Only if no APP is already available a create it from scratch.
2. In APP, I create a module named Module (usually it is a good idea choosing a better name). As you see, it is nothing more that an immediate function that receives in input three parameters, see the module last line (point 15.) to find out what they are.
3. The JavaScript code used is "strict".
4. This is a private variable (ideally, a constant) available to all the Module instances.
5. The Module's constructor. It accepts in input a variable used to set the internal object properties. In this case "options" contains just a single value, so it is an overkill not to use it. But usually options contains a large number of properties.
6. The infamous "this is that" pattern. Follow the link for more details.
7. Another well known JavaScript pattern. The constructor could be called improperly, with this check we avoid any unpleasant surprise.
8. We use jQuery, so to extends the "options" object passed to the constructor we use the jQuery .extend() function. Same functionality is provided by other frameworks, and it could also implemented with no special trouble by hands. The idea is that we want to ensure that our local "options" contains at least a property named "name". If the passed "options" has it, we will keep it, otherwise a new, empty one (or better, null), is created.
9. A private function. No one could access it outside its scope. Notice that inside it we have to use "that" to access the constructor "this".
10. A public function. It could be called from outside, and it has full access to the constructor and module properties.
11. As almost any constructor, at the end of its job it returns "this".
12. Sometimes it is useful to have a "static" (in the C++/Java sense) method in a module. Here is how we can emulate it. We can even use a trick (see 13.) to adapt this method so that it could also be called as a non-static function.
13. Here is the trick. We create a property in the Factory prototype, and we assign to it the static function. Neat.
14. Finally, the immediate function calls the constructor, so that the object is created.
15. We call the immediate function passing to it APP (the module's namespace), the current "this" (at this point it should be the global one), and jQuery (since we use it - you could pass your preferred framework instead, obviously. Or you could even pass many ones).

Here is a test usage for the above defined module:
var m1 = new APP.Module({name: 'John Smith'}); // 1
console.log(m1.getName());

var m2 = new APP.Module(); // 2
console.log(m2.getName());

APP.Module.aFunction(); // 3
console.log("Can't call a private function from outside:", m1._checkName === undefined);
console.log("Can't call an instance function from static context:", APP.Module.sayHello === undefined);
console.log("Can't call a static function from an object:", m1.aFunction === undefined);
m1.sayHello(); // 4
1. An object m1 is created, passing a name to the module.
2. Here we create an "anonymous" module, no name it is passed.
3. The static function is called on the module itself, it doesn't requires an actual instance.
4. But through that clever trick showed above on (13.) we can call the static function through an alias.

No comments:

Post a Comment