0

This is the code:

for (var i = 0; i <= 4; i += (i + 2)) {
    var arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];

    setTimeout(() => {
        console.log(arr[i]);
    }, 0); // add message to the queue
}

for (var k = 0; k <= 4; k += (k + 2)) {
    var arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];

    ((m) => setTimeout(() => console.log(arr[m]), 0))(k) // add message to the queue
}

for (let j = 0; j <= 4; j += (j + 2)) {
    let brr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];

    setTimeout(() => {
        console.log(brr[j]);
    }, 0); // add message to the queue
}

And the result is: gg from the first loop, ac from the second and third loop.

All callbacks will be put in the queue and will be invoked once the 3 loops are over. My question is why it is gg for the first loop and ac for the third loop and what would be the outer lexical environments at creation time for each callback passed as setTimeout argument?

pollx
  • 643
  • 9
  • 21
  • Since the difference is that the first one uses `var` and the last one uses `let`, I guess you're asking the difference between `let` and `var`? –  Nov 25 '17 at 17:16
  • 2
    https://stackoverflow.com/questions/44606868/difference-between-let-and-var-inside-settimeout –  Nov 25 '17 at 17:18

1 Answers1

0

The lexical scope of var i; is the window in this case (var is a function scope and not block scope).

So in the first loop, by the time the setTimeout callback is invoked, the i variable is already at the value of 6. Hence it will log g twice.

In the second loop you create a new function instance on each iteration that closes over the k variable, thus create a new instance of this variable in memory. when the setTimeout callback is invoked it has access to the relevant k object which is 0 is the first call and 2 in the second call.

In the third loop you are using let instead of var which uses the block scope.
So the same flow happens like in the second loop, but no need for a new function instance this time to close over the variable.

Edit
As a followup to your comment:

When the first callback is created i is in scope and has value of 0. Then at some point the callback is invoked and it checks out the value of i in it's outer lexical environment at time of creation. This 'at time of creation' bugs me , because the way i understand it , it would mean that i should be 0 , not 6, as actually it is.

The setTimeout callback doesn't know nothing about i when it was "created" it only accessing its value in memory when the callback is invoked.
The key point here to remember is that the setTimeout will invoke the callback only after the for loop has finished ALL iterations. This is because the setTimout is calling the callback in another event loop. I really recommend watching this video that explains in great details about the event loops.

Because i is in the same scope on each iteration the callback will get the final i "version" which is the value of 6.

Just to illustrate the situation, it's as if your code would look like this for the first example:

var i;

for(i =0; i < 4; i++){
  // not important 
}

console.log(i);
console.log(i);

In the other loops examples you create different new scope for each counter (k for the 2nd loop example and j for the 3rd example). And when the callback gets invoked it has access to the relevant scope.

Sagiv b.g
  • 30,379
  • 9
  • 68
  • 99
  • "So in the first loop, by the time the setTimeout callback is invoked, the i variable is already at the value of 6. Hence it will log g twice." – pollx Nov 25 '17 at 17:47
  • @pollx is there a question about this quote? – Sagiv b.g Nov 25 '17 at 17:50
  • yep, but SO doesnt let me save the edit to my post above. What I was saying is that I dont get clearly the part in the quotes. When the first callback is created i is in scope and has value of 0. Then at some point the callback is invoked and it checks out the value of i in it's outer lexical environment at time of creation. This 'at time of creation' bugs me , because the way i understand it , it would mean that i should be 0 , not 6, as actually it is. So 'at time of creation' we keep only the list of variables in scope or their corresponding values as well ? – pollx Nov 25 '17 at 18:05
  • The scope is not created at time of creation of the function, but time of invocation of the function. The outer function invokes immediately, meaning `k`'s *value* (rather than *reference*, since `k` is a primitive) is passed into the function right away. I would suggest you just never use `var`, as its lack of block scoping is confusing and error prone. – vox Nov 25 '17 at 18:28
  • "Whenever a function is created, a reference to the lexical environment in which the function was created is stored in an internal property named [[Environment]]" (book quote) The outer lexical environment is the environment at time of creation and it doest change. Every time the function is invoked a new lexical environment is created for that function and its variables and params. What I was missing about the let in for loop is: "i gets a new binding for every iteration of the loop.This means that every closure captures a different i instance" https://stackoverflow.com/a/35812376/7484241 – pollx Nov 25 '17 at 18:39