1

Please explain the difference between Both codes. Why Outputs of both are different.

'use strict';
let printNumTwo;
for (let i = 0; i < 3; i++) {
  if (i === 2) {
    printNumTwo = function() {
      return i;
    };
  }
}
console.log(printNumTwo());

'use strict';
let printNumTwo;
let i;
for (i = 0; i < 3; i++) {
  if (i === 2) {
    printNumTwo = function() {
      return i;
    };
  }
}
console.log(printNumTwo());
  • Have you seen this [question](https://stackoverflow.com/questions/762011/whats-the-difference-between-using-let-and-var-to-declare-a-variable-in-jav)? I'm sure if you read the accepted answer you could understand how `let` is scoped and hence why the output is different. – Yury Tarabanko Jun 19 '18 at 08:56
  • Thanks for sharing this link.. – Prakhar Agrawal Jun 19 '18 at 09:09

2 Answers2

3

The difference is in the scope where i was declared.

Declares i inside for loop block scope

'use strict';
let printNumTwo;
for (let i = 0; i < 3; i++) { // block scope
  // each time we have new variable i
  if (i === 2) { // block scope
    printNumTwo = function() {
      return i; // captures the one that was equal 2
    };
  }
}

// console.log(i) // throws ReferenceError
console.log(printNumTwo());

Declares i in global scope

'use strict';
let printNumTwo;
let i; // globally scoped
for (i = 0; i < 3; i++) {
  if (i === 2) {
    printNumTwo = function() {
      return i; // captures globally scoped i which will be mutated i++
    };
  }
}
console.log(i) // prints 3
console.log(printNumTwo());

UPD Some docs Spec

13.7.4.8 Runtime Semantics: ForBodyEvaluation ( test, increment, stmt, perIterationBindings, labelSet ) The abstract operation ForBodyEvaluation with arguments test, increment, stmt, perIterationBindings, and labelSet is performed as follows:

  1. Let V be undefined.
  2. Perform ? CreatePerIterationEnvironment(perIterationBindings).
  3. Repeat,

...

e. Perform ? CreatePerIterationEnvironment(perIterationBindings).

f. If increment is not [empty], then ...

Basically new PerIterationEnvironment is created before increment part. So function will capture i === 2.

Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
  • // captures the one that was equal 2, But it also increment the i to 3 – Prakhar Agrawal Jun 19 '18 at 09:11
  • "But it also increment the i to 3" not it. But the one created for the next step. You could read about how for loop body is evaluated [here](https://tc39.github.io/ecma262/2018/#sec-forbodyevaluation) Notice step `3.e.` new name binding context is created before `increment` part is performed. – Yury Tarabanko Jun 19 '18 at 09:27
1

I will explain only the first example because the second is obvious

'use strict';
let printNumTwo;
for (let i = 0; i < 3; i++) {
  if (i === 2) { // block scope
    printNumTwo = function() {
      return i;
    };
  }
}

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

when you use let inside for loop, it means that the variable i is only defined in the for scope, so in that scope when i is equal to 2 we set the variable printNumTwo, when we do i++ again, i change its value to 3 but it does not change its value inside the for scope, it remains equal to 2, which means if somehow if you wanna access the i value, it is always equal to 2

for (let i = 0; i < 3; i++) {
  console.log(i) // prints 0,1,2 
}

console.log(i) // undefined

The thing to remember is, even if when i is equal to 2 and we do i++, it does not change its value inside the for scope because i is defined with let

Mehdi Souregi
  • 3,153
  • 5
  • 36
  • 53