0

Why does closure behavior not come into play here:

function init(){
  for (var i = 1; i <= 2; i++) {
  
  function timer() {
    var k=i;
    console.log("in timer");
    console.log(timer.i);
    console.log(k);
  }
  
  timer.i = i;
  setTimeout(timer, 0);
  
  }
}
<body onload="init()";>
</body>

Why does timer.i not take the last value of i on the outer function init's stack? However, it does take the outer for k.

Alok D
  • 49
  • 7

2 Answers2

0

Function declaration in EcmaScript 6 are now block-scoped, just like let declarations (with the difference with let that the variable is declared in the outer global/functionnal scope).

Your code is globally equivalent to

for (var i = 1; i <= 2; i++) {
  let timer = function() {
    console.log("in timer");
    console.log(timer.i);
  }
  timer.i = i;
  setTimeout(timer, 1000);
}

This means you have different timer values.

Note that if you had used var instead of let and instead of a function declaration, you would have got twice the same log.

A good read: http://2ality.com/2015/02/es6-scoping.html

Please note that adding properties to a function isn't really a good practice. You should use scope variables for this, for example:

for (var i = 1; i <= 2; i++) {
  let timer_i = i;
  setTimeout(function timer(){
    console.log("in timer");
    console.log(timer_i);
  }, 1000);
}

which in this simple case could be just written as

for (let i = 1; i <= 2; i++) {
  setTimeout(function timer(){
    console.log("in timer");
    console.log(i);
  }, 1000);
}
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
0

There are actually two timers. When the interpreter runs across a function declaration inside a loop like that, it will overwrite the previous function name in the outer scope (if such a function exists). This snippet might make it clearer:

for (var i = 1; i <= 2; i++) {
  const oldTimer = window.timer;
  function timer() {
    console.log("in timer");
    console.log(timer.i);
  }
  timer.i = i;
  console.log('oldTimer is ' + oldTimer + (oldTimer ? ' with i of ' + oldTimer.i : ''));
  console.log('is oldTimer the same as the new timer? ' + (oldTimer === timer));
  setTimeout(timer, 1000);
}
console.log('Outside of for loop: timer is ' + typeof timer);

(The timer is still available outside of the for loop - it gets added to the global object.)

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • should both the timer.i 's not inherit the value of i from the outer? Even after the second timer block is defined/run over? – Alok D Jun 11 '18 at 09:22
  • No, because `timer.i` is assigned to the separate timers, synchronously, inside the loop. The `timer.i` is not a reference to the final value of the `i`, but a copy of the `i` at the moment it was assigned to the `i` property of the function. – CertainPerformance Jun 11 '18 at 09:25
  • True, it still confused me why it shouldn't think the same for var k=i, each var k=i also belongs to a different timer. Or basically it doesn't invoke the function so it does not do the assigment? Thanks for your help. – Alok D Jun 11 '18 at 09:35
  • I don't know what `k` you're referring to, but `var`s are hoisted and have unintuitive function scope rather than block scope (generally, always use `const` or `let` instead) – CertainPerformance Jun 11 '18 at 10:55