Tuesday 15 November 2016

Javascript: function expression

I often came across this pattern in javascrip code and never understood the advantage of it.

(function(window, document, undefined){
    window.MyObject = {
        methodA: function() { ... },
        methodB: function() { ... }
    };
})(window, document)

After reading You don't know JS (a great book), I finally came to understand this pattern. Here is a summary of my understanding.

This is how we normally define and call a js function:

var a = 2;

function foo(){
    var a = 3;
    console.log("inner a =" + a ); // 3
}; 

console.log("outer a = " + a ); //2

foo();

It is not ideal because we have to explictly call foo() to execute the function. If we just wish to call this method once and done, we have wasted one line of code.

Another issue is that it "pollutes the enclosing scope (quote the book)". I am personally OK with that. It's just one less variable name available for using.

Javascript has a solution to both problems:

var a = 2;

(function foo(){
    var a = 3;
    console.log("inner a =" + a ); // 3
})(); 

console.log("outer a = " + a ); //2

foo(); //ReferenceError

By wrapping the whole function with (), appending another pair of (), we are able to execute the function directly.

More significantly, now the variable foo is not accessible in global scope (Attemping to access it throws a refrenceError). In fact, foo is only accessible within the function scope.

Since it's unlikely to access foo variable within the function scope, anonymous function is preferred here.

(function (){
    var a = 3;
    console.log("inner a =" + a ); // 3
})();

A variation of this form is moving the second pair of () inside the first pair ()

(function (){
    var a = 3;
    console.log("inner a =" + a ); // 3
}());

These two forms are identical in functionality.

Parameter can be passed to the function. Now it looks more and more like the most common seen format in the js library code.


var a = 3;

(function (window){
    var a = 2;
    console.log( "a = "+a);
    console.log( "global a = "+window.a);    
})(window);

The book says another usage of the pattern is to address the issue of the value of undefined identifier being overwritten.

var undefined = true; // setting a land-mine for other code! avoid!

(function (){
    var a;
    if (a === undefined) {
        console.log( "a is undefined"); //still undefined
    }else{
        console.log( "a is defined" );
    }
})();

After experimenting, it turns out you can't overwrite the value of undefined in global scope -- You can only overwrite it within a function sceop. So in order to demonstrate the usage, I wrap the code with a function.

function foo(){

    var undefined = true; // setting a land-mine for other code! avoid!

    (function (){
        var a;
        if (a === undefined) {
            console.log( "a is undefined");
        }else{
            console.log( "a is defined" ); //becomes defined
        }
    })();
}

By defining an undefined paramater and not passing any value for that argument, undefined is guaranteed to be undefined.

function foo(){

    var undefined = true; // setting a land-mine for other code! avoid!

    (function (undefined){
        var a;
        if (a === undefined) {
            console.log( "a is undefined"); //Undefined, yeah!
        }else{
            console.log( "a is defined" );
        }
    })();
}