Modules

Now that we have a complete class system it would be good to address the global namespace discussed earlier. Again there is no first class support for namespaces but we can easily isolate functionality to the equivalent of a namespace. There are a number of different approaches to creating modules in JavaScript. We'll start with the simplest and add some functionality as we go along.

To start we simply need to attach an object to the global namespace. This object will contain our root namespace. We'll name our namespace Westeros; the code simply looks like:

Westeros = {}

This object is, by default, attached to the top level object so we need not do anything more than that. A typical usage is to first check if the object already exists and use that version instead of reassigning the variable. This allows you to spread your definitions over a number of files. In theory you could define a single class in each file and then bring them all together as part of the build process before delivering them to the client or using them in an application. The short form of this is:

Westeros = Westeros || {}

Once we have the object, it is simply a question of assigning our classes as properties of that object. If we continue to use the Castle object then it would look like:

let Westeros = Westeros || {};
Westeros.Castle = function(name){this.name = name}; //constructor
Westeros.Castle.prototype.Build = function(){console.log("Castle built: " +  this.name)};

If we want to build a hierarchy of namespaces that is more than a single level deep, that too is easily accomplished, as seen in this code:

let Westeros = Westeros || {};
Westeros.Structures = Westeros.Structures || {};
Westeros.Structures.Castle = function(name){ this.name = name}; //constructor
Westeros.Structures.Castle.prototype.Build = function(){console.log("Castle built: " +  this.name)};

This class can be instantiated and used in a similar way to previous examples:

let winterfell = new Westeros.Structures.Castle("Winterfell");
winterfell.Build();

Of course with JavaScript there is more than one way to build the same code structure. An easy way to structure the preceding code is to make use of the ability to create and immediately execute a function:

let Castle = (function () {
  function Castle(name) {
    this.name = name;
  }
  Castle.prototype.Build = function () {
    console.log("Castle built: " + this.name);
  };
  return Castle;
})();
Westros.Structures.Castle = Castle;

This code seems to be a bit longer than the previous code sample but I find it easier to follow due to its hierarchical nature. We can create a new castle using them in the same structure as shown in the preceding code:

let winterfell = new Westeros.Structures.Castle("Winterfell");
winterfell.Build();

Inheritance using this structure is also relatively easily done. If we were to define a BaseStructure class which was to be in the ancestor of all structures, then making use of it would look like this:

let BaseStructure = (function () {
  function BaseStructure() {
  }
  return BaseStructure;
})();
Structures.BaseStructure = BaseStructure;
let Castle = (function (_super) {
  __extends(Castle, _super);
  function Castle(name) {
    this.name = name;
    _super.call(this);
  }
  Castle.prototype.Build = function () {
    console.log("Castle built: " + this.name);
  };
  return Castle;
})(BaseStructure);

You'll note that the base structure is passed into the Castle object when the closure is evaluated. The highlighted line of code makes use of a helper method called __extends. This method is responsible for copying the functions over from the base prototype to the derived class. This particular piece of code was generated from a TypeScript compiler which also, helpfully, generated an extends method which looks like:

let __extends = this.__extends || function (d, b) {
  for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
  function __() { this.constructor = d; }
  __.prototype = b.prototype;
  d.prototype = new __();
};

We can continue the rather nifty closure syntax we've adopted for a class to implement an entire module. This is shown here:

var Westeros;
(function (Westeros) {
  (function (Structures) {
    let Castle = (function () {
      function Castle(name) {
        this.name = name;
      }
       Castle.prototype.Build = function () {
         console.log("Castle built " + this.name);
       };
       return Castle;
     })();
     Structures.Castle = Castle;
  })(Westeros.Structures || (Westeros.Structures = {}));
  var Structures = Westeros.Structures;
})(Westeros || (Westeros = {}));

Within this structure you can see the same code for creating modules that we explored earlier. It is also relatively easy to define multiple classes inside a single module. This can be seen in this code:

var Westeros;
(function (Westeros) {
  (function (Structures) {
    let Castle = (function () {
      function Castle(name) {
        this.name = name;
      }
      Castle.prototype.Build = function () {
        console.log("Castle built: " + this.name);
        var w = new Wall();
      };
      return Castle;
    })();
    Structures.Castle = Castle;
    var Wall = (function () {
      function Wall() {
        console.log("Wall constructed");
      }
      return Wall;
    })();
    Structures.Wall = Wall;
  })(Westeros.Structures || (Westeros.Structures = {}));
  var Structures = Westeros.Structures;
})(Westeros || (Westeros = {}));

The highlighted code creates a second class inside of the module. It is also perfectly permissible to define one class in each file. Because the code checks to get the current value of Westeros before blindly reassigning it, we can safely split the module definition across multiple files.

The last line of the highlighted section shows exposing the class outside of the closure. If we want to make private classes that are only available within the module then we only need to exclude that line. This is actually known as the revealing module pattern. We only reveal the classes that need to be globally available. It is a good practice to keep as much functionality out of the global namespace as possible.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset