Tuesday, August 14, 2012

Closures

Classic "last value" problem, when function declared within loop ends up using last value of an incrementing variable:

for (var i = 0; i < 10; i++)
{
    document.getElementById('box' + i).onclick = function()
    {
        alert('You clicked on box #' + i);
    };
}
This assigns 10 functions as event handlers to corresponding elements.Our intention is to have different `i` values in each of the event handlers. But this is not at all what happens in reality.

If you look closer, it's easy to see what's really going on. Each of the functions assigned to `onclick` has access to `i` variable. Since `i` variable is bound to the entire enclosing scope—just like any other variable declared with `var` or as function declaration—each of the functions have access to the same `i` variable. Once this snippet is executed, `i` variable keeps its last value—the one that was set when loop ended.

So how do we fix it? By taking advantage of closures, of course. The solution is simple: scope each of the `i` values to the level of event handler. Let's see how this is done:

for (var i = 0; i < 10; i++)
{
    document.getElementById('box' + i).onclick = (function(index) {
        return function() {
            alert('You clicked on box #' + index);
        };
    })(i);
}

OR

function makeHandler(index) 
{
    return function() 
    {
        alert('You clicked on box #' + index);
    };
}
for (var i = 0; i < 10; i++) 
{
    document.getElementById('box' + i).onclick = makeHandler(i);
}

No comments:

Post a Comment