Skip to content

Modular JavaScripting

slayerjay edited this page Aug 23, 2012 · 1 revision

JavaScript, being a prototype based, weakly typed and dynamic language is very powerful. But for those who come from a strict OO background of Java and C++, Object Orientation in JavaScript is a bit illusive. There are many ways to do object orientation in JavaScript. The purpose of this section is to introduce to you some of the patterns that we used in BoilerplateJS.

Why Object Oriented JS

Gone are the days where JS is used just as a scripting language to add some tweaks, validations and animations to a webpage. Today we are beginning to see the advent of many web applications with thick clients running on your browser where most of the application logic happens on your browser. This means that your JavaScript code becomes large in size, and it becomes difficult to manage. Writing Object Oriented code helps you to loosely couple your code, and make it flexible.

JS Object Orientation Patterns

Take a look at the following code; you will notice that this looks a lot similar to your traditional OO languages.

// in JS, functions can be used as OO classes
var Person = function(name) {
    var self = this;
    
    //private methods and attributes
    this.getNickname = function(){
        return "Yaka";
    };
    
    //public methods and attributes (return obj)
    return {
        getName : function() {
            return name + ' ' + self.getNickname();
        }
    };
};

//static functions attached to 'Person' class 
Person.hasBrain = function() {
    return true;
};

var developer = new Person("Hasith");

developer.getName();    //"Hasith Yaka"
developer.getNickname;  //undefined
developer.hasBrain;     //undefined, because static functions shouldn't be exposed via instances
Person.hasBrain;        //is a function
Person.hasBrain();      //true

This pattern allows you to define private, public and static variables and functions. Several things happen during the call to var developer = new Person("Hasith");

  1. It creates a new object of type Object.
  2. Each object has an internal property called prototype. When a function is called with the new keyword, it sets the value of prototype of the new object, to the function’s prototype.
  3. By default, if we did not return any value form within the function, this is returned. Therefore the instance will have access to all the functions and variables that were bound to this variable in the above function.
  4. But in this case, we return an object from within the Person function. Therefore only the returned object is assigned to the variable developer. Instead of exposing the whole function space, we now only expose a set of functions and variables. This effectively makes all the other variables and functions inaccessible making them private.
  5. Then the function Person is executed.

This pattern is does have its disadvantages. Instances of methods are created per each object that is created. So it has a performance impact if a large amount instances are created.

Prototype Based Object Orientation

An improved method, where functions are attached to the prototype of an object is used in the core classes of BoilerplateJS to improve the performance.

// in JS, functions can be used as OO classes
var Person = function(name) {
    this.name = null;
};

// use of prototypes improves performance and memory use
Person.prototype.getName = function() {
    return this.name;
}

Person.prototype.setName = function(name) {
    this.name = name;
}   
    

var Manager = function() {}
Manager.prototype = new Person();


var manager1 = new Manager();
manager1.setName("mgr one");

var manager2 = new Manager();
manager2.setName("mgr two");

    
manager1.getName(); //"mgr one", one prototype instance, but different behavior
manager2.getName(); //"mgr two", one prototype instance, but different behavior

The advantage of this pattern, is that only one instance of the functions is created. The downside is that for those who are used to the traditional OOP approach, the readability of the code is pretty low.

Asynchronous Module Definition (AMD)

Then next practice that we bring in from the OO world is “one file per class/object” concept. Having the classes in separate classes improves modularity, and makes it easy to work on projects with a team. However managing such files and managing dependencies manually is a nightmare. This is where RequireJS comes into help. RequireJS is a JavaScript file and module loader. It manages dependencies, and also provides optimization when deploying. The main purpose of this section is to provide you with a basic understanding of RequireJS. For more information please refer RequireJS website.

The two main functions of RequireJS that you will be using are require and define.

require function is used to load script files perform some computations.

requirejs(['jquery', 'lib/module'], function   ($, Module) {
    //jQuery and lib/module are now loaded, and can be used as
    //$ and Module
});

define function is used to define modules. This is how you will be defining your application components and modules within BoilerplateJS.

define(['dependancy1', 'dependancy2'], function(Dependancy1, Dependancy1) {
    //Static things that are common for all instances goes here
    
    //Define the MyModule
    var MyModule = function() {
        this.myMethod = function(){
            //a public method in MyModule
        }
    };

    return MyModule;
});

This pattern is also described in http://requirejs.org/docs/api.html#funcmodule

You can also return objects as follows:

define(function () {
    //Do setup work here

    return {
        color: "black",
        size: "unisize"
    }
});

This pattern is further described in http://requirejs.org/docs/api.html#deffunc

Further References:

  1. https://developer.mozilla.org/en-US/docs/Introduction_to_Object-Oriented_JavaScript

  2. https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Details_of_the_Object_Model