Saturday, September 22, 2012

Closures in JavaScript

Closures is a kind of object which combines 2 things - a function and the environment in which this function is been created. In simple words when we define an inner function, and that inner function uses the any variable defined outside(say in its outer function), then it is known as closure. Because even if the outer function returns then also this local variable is still in use by the inner function.
Lets understand it step by step using examples -

    function Outer(){
        var local = 1;
        return function(){
            return local;
        }
    }
    var outVar = Outer();
    alert(outVar());
In the above example if you see once the outVar is defined, the Outer function get returns. Then also the value local is available via inner anonymous function, This is simplest example of closure.

check the following scenario -


  var outVar ;
    function Outer(){
        var local = 1;
        outVar =  function(){
            return local;
        }
    }
    alert(typeof outVar);//undefined
    Outer();
    alert(typeof outVar); //function
    alert(outVar()); //1
Above example is very much similar to the 1st one.


Lets complicate the above example a little bit -

    var outVar ;
    function Outer(){
        var local = 1;
        outVar =  function(){
            return local;
        }
        local = 2;
    }   
    Outer();  
    alert(outVar()); //2
If you notice in above example, after assignment we have modified the value of local to 2, and once you call the closure, it alerts 2 not 1. It means the inner function is actually using the same local object of outer function, it didn't create a local copy of it.
Lets check one more example to understand it little deeper -

    var outVar1,outVar2 ;
    function Outer(){
        var local = 1;
        return function(){
            local++;
            return local;
        }
    }
    outVar1 = Outer();
    outVar2 = Outer();
    outVar1();
    outVar2();
    alert(outVar1());//--1
    alert(outVar2());//--2
In the above example, what you think will be the alert in statement 1 and 2, as both outVar1 and 2 are assigned the same inner function. Well the answer is both of them will alert 3, as they have their own copy of local variable(because local is actually a local variable of outer function).

Analyze the following code -

    function make(){
        var i;
        var a=[];
        for(i=0;i<3 i="i" p="p">            a[i] = function(){
                return i;
            }
        }
        return a;
    }
    var funcs = make();
    alert(funcs[0]()); //--1
    alert(funcs[1]()); //--2
    alert(funcs[2]()); //--3
What you think will be the output of above code?
At first you will think it might be 1,2,3 ....but actually it will alert 3,3,3. This is because the inner function get bound with the local variable i, and once we assign it to array, it actually got assigned( not evaluated or not executed). Thus once we execute it, it actually refer to same i from its outer function, and as its state got changed to 3, thus it alerts 3.
Now how can we fix this problem? Here is the fix -

function make(){
        var i;
        var a=[];
        for(i=0;i<3 i="i" p="p">            a[i] = (function(input){
                return function(){
                    return input;
                };
            })(i);
        }
        return a;
    }
    var funcs = make();
    alert(funcs[0]());
    alert(funcs[1]());
    alert(funcs[2]());
If analyze the code we have created another function which takes input as i, and then pass it further down to another function. Now the inner function or the closure is actually not connected to i directly.
Above code is very important to understand, we see application of closure in our daily javascript coding. Here is an example on the same concept where we are using the same concept -


for(i=0;i<3 i="i" p="p">            document.getElementById('btn'+i).onclick = function(){
                alert(i);
            }
        }
it will have the same issue, it will alert only 3. Here is the corrected version -

 document.getElementById('btn'+i).onclick = (function(input){
                return function(){
                    alert(input);
                }
            })(i);






No comments:

Post a Comment