views:

108

answers:

2

Check out the following snippet of HTML/Javascript code:

<html>
<head>
<script type="text/javascript">
var alerts = [];
for(var i = 0; i < 3; i++) {
    alerts.push(function() { document.write(i + ', '); });
}

for (var j = 0; j < 3; j++) {
    (alerts[j])();
}

for (var i = 0; i < 3; i++) {
    (alerts[i])();
}
</script>
</head><body></body></html>

This outputs:

3, 3, 3, 0, 1, 2

which isn't what I was expecting - I was expecting the output 0, 1, 2, 0, 1, 2,

I (incorrectly) assumed that the anonymous function being pushed into the array would behave as a closure, capturing the value of i that's assigned when the function is created - but it actually appears that i is behaving as a global variable.

Can anyone explain what's happening to the scope of i in this code example, and why the anonymous function isn't capturing its value?

+5  A: 

In Javasript, the only "interesting" lexical scope boundary is the function body. Anything declared anywhere in a function (well, anywhere other than another nested function!) is at the same scope. There are also some weird things about the way that the declarations are interpreted.

Your anonymous function does act as a closure, but each function instantiated will share the same "i". A trick I use is to add another layer of function:

for (var i = 0; i < whatever; i++) {
  (function(idaho) {
    whatever(function() { alert("my own private " + idaho); });
  })(i);
}

At somepoint hopefully all the browsers will support the new "let" statement, which is a shorter, less weird-looking way to do basically the same thing.

Pointy
+5  A: 

The scope is the function that the variable is defined in (except there isn't one, so it is global).

The anonymous function you are passing is accessing the variable defined in the parent function's (again global) scope.

You need an actual closure.

alerts.push(
    function (foo) { 
        return function() { 
            document.write(foo + ', ');

        }
    }(i)
);
David Dorward
Correct answer - thanks! - but I've accepted Pointy's answer because "my own private " + idaho made me laugh out loud...
Dylan Beattie