Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

let keyword in the for loop

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.

like image 637
Krzysztof Szafranek Avatar asked May 10 '13 00:05

Krzysztof Szafranek


People also ask

Can we use let in for loop?

According to MDN using let in the for loop like that should bind the variable in the scope of the loop's body.

What is the let keyword used for?

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.

Should I use let or const in for loop?

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.

What is the difference between VAR and let in for loop?

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.


2 Answers

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.

like image 111
neuron Avatar answered Oct 07 '22 00:10

neuron


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 _is in other scopes.

    (You can confirm that there are three different _is 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.

like image 28
joeytwiddle Avatar answered Oct 07 '22 01:10

joeytwiddle