This is correct behavior according to the spec. The behavior with var
and let
is defined to be different.
See the spec, at https://people.mozilla.org/~jorendorff/es6-draft.html#sec-forbodyevaluation. According to this, the relevant concepts, which make the function declared inside the loop close over the current value of the block-scoped loop index, are things called "per-iteration bindings" and "per-iteration environment".
Babel handles it correctly, producing the following code:
var a = [];
var _loop = function (i) {
a[i] = function () {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
This implements the semantics of for (let
by isolating the contents of the for
loop into a separate function parameterized by the index. By virtue of doing that, the function no longer closes over the for loop index, and i
is treated separately in each function created. Thus the answer is 6.
Traceur does not produce the correct result. It yields 10.
So the famous question that has been asked 100 times on SO, about why my function declared in a loop and closing over the index index is using the "wrong" value of the loop index, shall be asked no more?
The issue is a bit more nuanced that merely proclaiming that "of course, let
is block-scoped". We know that. We get how it works in an if
block, for example. But what's going on here is a bit of an twist on block scoping in the context of a for
, hitherto unknown to many people including me. It's a variable actually declared outside the "block" (if you think of the block as the body of the for
statement) but has a separate existence inside each iteration of the loop.
For more, see https://github.com/babel/babel/issues/1078.