JavaScript Closure

Like my other posts, I prefer to use actual code snippets, examples to explain a concept. This one is probably the most favorable JavaScript interview question, as well as a fundamental concept to learn in order to really nail JavaScript. When you read here, you must've already see tons of blogs, articles which are repeating the definition about JavaScript closure, something like

"Inner function has access to those local variables even after the outer function has finished executing and no longer exists.".

Don't get me wrong, this is the absolutely right explanation. What I am trying to do here is to give you a few more actual cases to help you realize this particularly interesting concept you've already used in your projects (definitely).

1) Simple closure example:

function foo() {
    var bar = "GeniusCarrier";
    function baz() {
        console.log(bar);
    }
    baz();
}
foo(); // GeniusCarrier

Note: This is an example of functional scoping: in JavaScript, the scope of a variable is defined by its location within the source code, and nested functions have access to variables declared in their outer scope.

function foo() {
    var bar = "GeniusCarrier";
    function baz() {
        console.log(bar);
    }
    return baz();
}
var qux = foo(); 
qux(); // GeniusCarrier

Note: Inner function has access to the local variables even after the outer function returns.

2) Ajax closure example:

function foo(bar, baz) {
    $.ajax({
        type : "GET",
        url : bar,
        dataType : "jsonp",
        success : function(data) {
            callback(data, baz);
        }
    }); 
};
var callback = function(data, baz) {
    console.log(baz + ":" + data);
} 

or

function foo(bar, baz) {
    $.ajax({
        type : "GET",
        url : bar,
        dataType : "jsonp",
        success : callback(baz)
    }); 
};
var callback = function(baz) {
    return function(data) {
        console.log(baz + ":" + data);
    }
} 

Note: It is a very common practice about how to use closure to pass an argument to callback function.

3) Closure in a for-loop example:

function foo() {
    var bar = [a,b,c];
    for (var i = 0; i < bar.length; i++) {
        var baz = bar[i];
        baz.onclick = function() {
            console.log(i);
        };
    }
}
foo(); // 2,2,2

Fix:

function foo() {
    var bar = [a,b,c];
    for (var i = 0; i < bar.length; i++) {
        var baz = bar[i];
        baz.onclick = (function(index) {
            retrun function() {
                console.log(index);
            }
        })(i);
    }
}
foo(); // 0,1,2

Note: JavaScript does not have block scope, but a lexical scope that is captured by functions. By creating an anonymous function, we introduce a new variable scope for each iteration.

4) Ajax in a for-loop closure example:

function foo(bar) {
    for(var i = 0; i < 3; i++) {
        $.ajax({
            type : "GET",
            url : bar,
            dataType : "jsonp",
            success : callback(i)
        });
    }
};
var callback = function(index) {
    return function(data) {
        console.log(index + ":" + data);
    }
} 

Note: This is a special case of closure in a for-loop example.

5) Emulating private methods with closure exmaple:

var foo = (function() {
    var privateVar = 0;
    function privateFunc() {
        privateVar++;
    }
    return {
        publicGet: function() {
            return privateVar;
        },
        publicSet: function() {
            privateFunc();
        }
    }
})();

console.log(foo.publicGet); // 0
console.log(foo.publicSet);
console.log(foo.publicGet); // 1

Note: Neither privateVar nor privateFunc can be accessed directly from outside the anonymous function. Instead, they must be accessed by two public functions (publicGet, publicSet) that are returned from the anonymous wrapper.

Conclusion

"JavaScript functions are a combination of code to be executed and the scope in which to execute them. This combination of code and scope is known as a closure in the computer science literature. All JavaScript functions are closures. These closures are only interesting, however... when a nested function is exported outside the scope in which it is defined. When a nested function is used in this way, it is often explicitly called a closure."

- JavaScript, The Definitive Guide, 8.8.4, by David Flanagan

Yang Zhao

Read more posts by this author.


Comment