0

Okay. I know Javascript uses function scoping versus block scoping, but I cannot figure out why this example of this behavior works like it does.

var txt = ["a","b","c"];

for (var i = 0; i < 3; ++i ) { 
    var msg = txt[i];
    setTimeout(function() { console.log(msg); }, i*1000);        
}​
//outputs c, c, c

It seems to me that if i is being "hoisted" to the top of this code block then i would equal 0, then 1000, then the for loop would break is because 1000 > 3. So why does this loop behave the way it does?

EDIT: I know this has something to do with msg = txt[i], because the code works like it seems it should when:

var txt = ["a","b","c"];

for (var i = 0; i < 3; ++i ) { 
    let msg = txt[i];
    setTimeout(function() { console.log(msg); }, i*1000);        
}
jellies
  • 639
  • 1
  • 5
  • 17
  • Why would `i` ever be `1000` ? You are doing `++i` which increments `i` by `1`. But yes, `i` is hoisted, and so is `msg`. By the time the first `setTimeout` callback is called, `msg` has the value `"c"`. – Felix Kling Apr 17 '17 at 04:12
  • @FelixKling can you **please** explain that in more depth? I looked at the duplicate you pinned this with and it seems different to me. I cannot extrapolate how they cause the same problem, though I'm sure they do. – jellies Apr 17 '17 at 04:27
  • Not sure what more there is to explain. There is no difference between the scope of `i` and `msg`. The loop iterates three times. In each iteration you are assigning `msg = txt[i]`, then *after* the loop terminated, the first `setTimeout` callback is called. It logs the value of `msg` which is the value that was assigned in the *last iteration*. If `msg` confuses you then remove it and log `i` directly. You will get `3,3,3` because when the first `setTimeout` callback is called, `i` has the value `3`. – Felix Kling Apr 17 '17 at 04:31
  • @FelixKling oh! My misunderstanding lies in how setTimeout works then. I did not realize it a) once it gathers a value, does not obstruct the flow of the function and, b) assigns values at the end of the timeout rather than the beginning. – jellies Apr 17 '17 at 04:35
  • yeah, all that `setTimeout` does is adding functions to the event loop, but the next function in the loop is only executed *after* the current execution context terminated (i.e. the loop). Have a look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop to learn more about that. In addition to that, closures evaluate their free variables when the are *called*, not when they are created. – Felix Kling Apr 17 '17 at 04:43

0 Answers0