Maintainable Javascript Part Two

Maintainable Javascript continue, you can read Part One to review.

Avoid Globals

Global variables are really special in Javascript, see examples below.

// A "mistake" semicolon creates a global variable
function doSomething() {
    var count = 10; 
        title = "Maintainable Javascript"; 
}

// This global variable affects window.name
function doAnotherThing() {
    var count = 10;
        name = "Tinple"; 
}

And the author recommends that anything that can be defined within the function should be written as such; any data that comes from outside the function should be passed in as an argument. Besides, it is better to use The One-Global Approach, like some popular JavaScript libraries.

// only one gloabal object
var MaintainableJS = {};

MaintainableJS.Book = function(title) {
    this.title = title;
    this.page = 1;
};

MaintainableJS.Book.prototype.turnPage = function(direction) {
    this.page += direction;
};

MaintainableJS.Chapter1 = new MaintainableJS.Book("part-one");
MaintainableJS.Chapter2 = new MaintainableJS.Book("part-two");
MaintainableJS.Chapter3 = new MaintainableJS.Book("part-three");

Namespace

And you can easily create your own namespaces in JavaScript with objects.

var Books = {};

// namespace for this book
Books.MaintainableJavascript = {};

//namespace for another book
Books.HighPerformanceJavascript = {};

There are also times when each file is simply adding to a namespace.

var YourGlobal = {
    namespace: function(ns) {
        var parts = ns.split("."),
            object = this,
            i, len;

        for (i = 0, len = parts.length; i < len; i++) {
            if (!object[parts[i]]) {
                object[parts[i]] = {};
            }
            object = object[parts[i]];
        }

        return object;
    }
};

/*
* Creates both YourGlobal.Books and YourGlobal.Books.MaintainableJavaScript.
* Neither exists before hand, so each is created from scratch.
*/
YourGlobal.namespace("Books.MaintainableJavaScript");

// you can now start using the namespace
YourGlobal.Books.MaintainableJavaScript.author = "Nicholas C. Zakas";

/*
* Leaves YourGlobal.Books alone and adds HighPerformanceJavaScript to it.
* This leaves YourGlobal.Books.MaintainableJavaScript intact.
*/
YourGlobal.namespace("Books.HighPerformanceJavaScript");

// still a valid reference
console.log(YourGlobal.Books.MaintainableJavaScript.author);

// You can also start adding new properties right off the method call
YourGlobal.namespace("Books").ANewBook = {};

Modules

Sorry, but I will try to understand it entirely later.You can read some documents at will.

Sea.js

Require.js

Event Handling

Event handling is an important part of any JavaScript application. All JavaScript is tied to the UI through events.And the following rules may improve your code.

// Rule #1
// It is better to separate application logic
var MyApplication = {

    handleClick: function(event) {
        this.showPopup(event);
    },

    showPopup: function(event) {
        var popup = document.getElementById("popup");
        popup.style.left = event.clientX + "px";
        popup.style.top = event.clientY + "px";
        popup.className = "reveal";
    }
};

addListener(element, "click", function(event) {
    MyApplication.handleClick(event);
});

// Rule #2
// Do not Pass the Event Object Around
var MyApplication = {

    handleClick: function(event) {

        // assume DOM Level 2 events support
        event.preventDefault();
        event.stopPropagation();

        // pass to application logic
        this.showPopup(event.clientX, event.clientY);
    },

    showPopup: function(x, y) {
        var popup = document.getElementById("popup");
        popup.style.left = x + "px";
        popup.style.top = y + "px";
        popup.className = "reveal";
    }
};

addListener(element, "click", function(event) {
    MyApplication.handleClick(event);
});

Avoid Null Comparisons

  • Use typeof operator to detect Primitive Values (string, number, boolean, undefined)
  • Use instanceof operator to detect Reference Values (Obejct, Array, Date, Error, RegExp)
  • Use typeof operator to detect Functions (also works across frames but not IE8)
  • Use in operator to detect the presence of a property (hasOwnProperty() to make sure of an instance property)
  • Detect Array
function isArray(value) {
    if (typeof Array.isArray === "function") {
        return Array.isArray(value);
    } else {
        return Object.prototype.toString.call(value) === "[object Array]";
    }
}

Maintainable Javascript Part Two over.