Last Updated: February 25, 2016
·
370
· dylanlukes

Properly Scoping Loop Variables

Take the following example:


var foos = ["foo0", "foo1", "foo2"];

for (var i = 0; i < 3; i++) {
  var foo = foos[i];
  setTimeout(function() {
    console.log(foo);
  }, 0);
}

You'd think it would print foo0, foo1, and foo2 right? Nope. You'll get foo2 three times. This is because foo inside the closure passed to setTimeout is the same reference.

The timeout calls occur asynchronously, and by the time they do, it's almost certain foo will have reached its final value of foo2. There are a couple ways around this. One is to use an array in place of foo that stores the values for each iteration separately. This doesn't really make the problem much nicer though.

CoffeeScript's solution is the arcane do keyword, which creates a new function scope, punning its parameters against the current local scope. For instance, do (i) -> ... is just (function(i) { ... })(i).

Even if you aren't using CoffeeScript, you can solve this problem in the same manner. Just introduce another scope like so:

var foos = ["foo0", "foo1", "foo2"];

for (var i = 0; i < 3; i++) {
  (function() {
    var foo = foos[i];
    setTimeout(function() {
      console.log(foo);
    }, 0);
  })();
}

Now there are multiple foo variables each within their own scope, with the behavior one would expect coming from a language with block scoping.