3

Anyone explain this(behavior differences between var and let in for loop) please?

  1. classical interview question(closure):

    let a = [];
    for (var i=0; i<10; i++) {
        a[i] = function() {
          console.log(i,);
        }
    }
    a[0](); // 10
    a[1](); // 10
    
  2. if we use let:

    let a = [];
    for (let i=0; i<10; i++) {    // var => let
        a[i] = function() {
        console.log(i);
      }
    }
    
    a[0](); // 1
    a[1](); // 2
    

Yes, i is normal and as expected when using 'let'. The first thought came to my mind is that let doesnt support closure. But no, it supports. (following tested it on Chrome):

function bb() {
  let b = 1;
  return function() {
    console.log(b);
  }
}
bb()();    // 1, means a closure is created

Then my second explanation is: when using let in for loop. For EVERY loop, it creates a new i repeatedly. And this is proved in chrome debugger by me.

Question is:

if i is created in each loop in the for-loop. How does it keeps increasing??? Why it doesn't just keep being 0 as we declared if it is created every time?

for (let i=0; .......)
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
qweszxcj
  • 153
  • 8
  • @thefourtheye Thanks for your lighting fast editing!!! – qweszxcj Aug 06 '16 at 06:31
  • Maybe take another look at [this question](http://stackoverflow.com/questions/762011/let-keyword-vs-var-keyword?rq=1) – Wickramaranga Aug 06 '16 at 06:36
  • Note that IE doesn't do `let` properly when used in the parentheses of the `for` (but does get it right if `let` is used in the body of the `for`). – nnnnnn Aug 06 '16 at 07:30
  • @Wickramaranga it's a good one with covering most of the aspects. thx! – qweszxcj Aug 06 '16 at 21:51
  • @nnnnnn Thanks for pointing out 'let in IE'. I think it explains something. That means parentheses is somehow different from { ... } – qweszxcj Aug 06 '16 at 21:53

2 Answers2

6

From your example, the environment for the "lexical declaration" let i is being created every iteration. However, the for loop uses the previous environment in order to create the next environment. So when the increment occurs it begins where it left off. Otherwise using let to declare i would create an infinite loop (i.e. i would always be <10).

How it works:

In the for loop body evaluation, the step after 'test' and 'result' is CreatePerIterationEnvironment(perIterationBindings) which declares a new environment and initializes all the bindings to their last known value: thisIterationEnv.InitializeBinding(bn, lastValue). Once complete the new environment is set as the current environment and the 'increment' step begins.

Definition:

InitializeBinding(N,V) Set the value of an already existing but uninitialized binding in an Environment Record. The String value N is the text of the bound name. V is the value for the binding and is a value of any ECMAScript language type.

Shawn Moore
  • 453
  • 3
  • 8
1

The first one, the wrong(unexpected) result is just because closures have access to the updated values of the outer function’s variables(finally the variable 'i' in closures is 10);


The second one, use let to declare variable i to mark it as a block variable which means the closures get the immediate i(like IIFE).

liuwenzhuang
  • 946
  • 7
  • 14