1

I'm trying to understand how scopes work in JS. Can you tell me if I correct with understanding of this chunk of code:

for(var i = 0; i<5; i++){
 setTimeout(function timeoutHandler() {
   var i = i;
   console.log(i); //undefined
 })
}

console.log prints undefined 5 times. So as I understand, when timeoutHandler is executed, it has its own local scope with own i variable. So is that correct that during timeoutHandler execution, the interpreter asks for i variable, finds it declared in local scope and uses this declared variable for assignment? This explains why it is undefined, but I'm not sure I'm fully correct. Thanks

UPD I don't need this code to work properly, I just want to understand why it behaves like that in this case

  • `var i = i` inside the setTimeout callback handler creates a new scope for `i` and `i` hasn't been defined yet before assigning it to `i` again. Hence `i` is `undefined`. – gurvinder372 Oct 20 '17 at 10:42
  • Firstly `var i = i` doesn't make sense. – evolutionxbox Oct 20 '17 at 10:42
  • `var i = i` is equivalent to `var i; i = i;`. – JJJ Oct 20 '17 at 10:43
  • Try passing in the `i` as a param of the `setTimeout` – evolutionxbox Oct 20 '17 at 10:43
  • @JJJ which would explain why `i` gets set to `undefined`. – evolutionxbox Oct 20 '17 at 10:44
  • Thank you for responses. Just to make clear I don't use this code anywhere and don't need it to work properly. I just wanted to understand why it behaves like that it this case. – Vlad Shevtsov Oct 20 '17 at 10:49
  • `setTimeout` is asynchronous. It gets set five times in the loop, but isn't fired until the end of the loop. While the function inside it has it's own scope with it's own `i`. That's the reason this will print `undefined`. If `var i = i` would be left out, console would print `5` since that is the final result of the loop before `setTimeout` fires. – Mouser Oct 20 '17 at 11:03
  • As @Mouser said, i ends up being set to 5 for each setTimeout callback. Maybe you're already familiar with this, but your code might behave more intuitively if you used `let` instead of `var`. let uses block scoping, and the numbers will iterate in the setTimeout function the way you would expect. – Michael Beeson Oct 20 '17 at 11:10

1 Answers1

0

Trying to make sense of it by this example:

for(var i = 0; i<5; i++){
//create a self executing function 
 (function(iValue){
  //in this scope iValue has the i from the loop
  setTimeout(function() {
   console.log(iValue); //will print the correct i
 })
 })(i); //pass i in it's current context to the function
}

Or easier with ES6: Use let instead of var. Since let is scoped within the block (each run of the loop is it's own block) it will not get another value inside the setTimeout.

for(let i = 0; i<5; i++){
  setTimeout(function() {
   console.log(i); //will print the correct i
 })
}
Mouser
  • 13,132
  • 3
  • 28
  • 54