ECMAScript 6's let
is supposed to provide block scope without hoisting headaches. Can some explain why in the code below i
in the function resolves to the last value from the loop (just like with var
) instead of the value from the current iteration?
"use strict"; var things = {}; for (let i = 0; i < 3; i++) { things["fun" + i] = function() { console.log(i); }; } things["fun0"](); // prints 3 things["fun1"](); // prints 3 things["fun2"](); // prints 3
According to MDN using let
in the for
loop like that should bind the variable in the scope of the loop's body. Things work as I'd expect them when I use a temporary variable inside the block. Why is that necessary?
"use strict"; var things = {}; for (let i = 0; i < 3; i++) { let index = i; things["fun" + i] = function() { console.log(index); }; } things["fun0"](); // prints 0 things["fun1"](); // prints 1 things["fun2"](); // prints 2
I tested the script with Traceur and node --harmony
.
According to MDN using let in the for loop like that should bind the variable in the scope of the loop's body.
The let keyword is used to declare variables in JavaScript. The var keyword can also be used to declare variables, but the key difference between them lies in their scopes. var is function scoped while let is block scoped - we will discuss this in more detail later.
A for loop's control variable is normally not constant (since in the normal case you update it in the "update" clause of the for ; if you don't, for may be the wrong loop to use), so you normally use let with it.
let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
squint's answer is no longer up-to-date. In ECMA 6 specification, the specified behaviour is that in
for(let i;;){}
i
gets a new binding for every iteration of the loop.
This means that every closure captures a different i
instance. So the result of 012
is the correct result as of now. When you run this in Chrome v47+, you get the correct result. When you run it in IE11 and Edge, currently the incorrect result (333
) seems to be produced.
More information regarding this bug/feature can be found in the links in this page;
Since when the let
expression is used, every iteration creates a new lexical scope chained up to the previous scope. This has performance implications for using the let
expression, which is reported here.
I passed this code through Babel so we can understand the behaviour in terms of familiar ES5:
for (let i = 0; i < 3; i++) { i++; things["fun" + i] = function() { console.log(i); }; i--; }
Here is the code transpiled to ES5:
var _loop = function _loop(_i) { _i++; things["fun" + _i] = function () { console.log(_i); }; _i--; i = _i; }; for (var i = 0; i < 3; i++) { _loop(i); }
We can see that two variables are used.
In the outer scope i
is the variable that changes as we iterate.
In the inner scope _i
is a unique variable for each iteration. There will eventually be three separate instances of _i
.
Each callback function can see its corresponding _i
, and could even manipulate it if it wanted to, independently of the _i
s in other scopes.
(You can confirm that there are three different _i
s by doing console.log(i++)
inside the callback. Changing _i
in an earlier callback does not affect the output from later callbacks.)
At the end of each iteration, the value of _i
is copied into i
. Therefore changing the unique inner variable during the iteration will affect the outer iterated variable.
It is good to see that ES6 has continued the long-standing tradition of WTFJS.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With