0

I wrote a simple javascript code. My for loop iterates a "let" declared variable, i between 0 and 2. A function gets declared within the loop only when i == 2. The function has to return the value of i variable. When I call this function from outside the loop, the function returns the value of i = 2 (which is natural for a block scope variable i. However, when I rewrite the loop code as its non-loop equivalent code-block, the function (still called from outside the block) returns the vale of i = 3. What is going on?

"use strict";
var printNumTwo;

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

//  loop equivalent
{
  let i = 0;
  i = 1;
  i = 2;
  printNumTwo = function() {
    return i;
  }
  i = 3;
}
console.log(printNumTwo());     // returns 3
  • Just for the sake of adding a little boilerplate to the answers below, you can also use a **self invoking anonymous function** to return an **anonymous function** where `i` is referenced as an argument. In that way, `i` will be isolated to the function's scope, hence it will log 2: http://jsfiddle.net/briosheje/mho6wgxn/ Besides, you should take a look at this: https://stackoverflow.com/questions/762011/whats-the-difference-between-using-let-and-var-to-declare-a-variable-in-jav – briosheje Dec 28 '18 at 12:08
  • Try changing your function var to `const printNumTwo` – mazunki Apr 16 '19 at 23:41

5 Answers5

1

Your example is bad because your loop is not counting after 2. So If your loop looks like i <= 3:

for (let i = 0; i <= 3; i++) {
  if (i === 2) {
    printNumTwo = function() {
      return i;
    };
  }
}

You would get exactly same result as your non-loop example and that's because of closure in javascript but return breaks for loop. Your function is saving reference to that variable from outside scope.

Kalix
  • 25
  • 5
  • Thanks for your reply but return doesn't break the loop, it only breaks the function scope. The loop still continues to execute till the last iteration. Also, the reason I did't use i <= 3 is because i becomes 3 anyway before the loop ends. – alphonsorob Jan 18 '19 at 06:10
0
  1. In your non loop based code, printNumTwo is not executed at the same point of its declaration and so the value of i is updated before it is executed so the value 3 is returned.

{
    let i = 0;
    i = 1;
    i = 2;
    printNumTwo = function () {
        return i;
    }
    i = 3;
}
console.log(printNumTwo());
  1. but if you run the following code, it should print 2 since it is executed before value if i is set to 3

{
    let i = 0;
    i = 1;
    i = 2;
    printNumTwo = (function() {
        console.log(i);
    })()
    i = 3;
}

Note: return in for loop breaks the further execution of the loop, so even if your first code had i <= 3 as its breaking condition, it will return 2.

for (let i = 0; i <= 3; i++) {
    if (i === 2) {
        printNumTwo = function() {
          return i;
        };
    }
}
console.log(printNumTwo())
Avi
  • 2,014
  • 1
  • 9
  • 21
0

It's because you're actually setting the function to return the value 3 because of the non-loop environment. You should change the loop a little, adding another variable, but first make your function look like this:

printNumTwo = function() {
    return num;
}

And in your simulated loop:

i = 2;
num = i;
printNumTwo = function() {
    return num;
}
i = 3;
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
0
"use strict";
var printNumTwo;

for (let i = 0; i < 3; i++) {
    printNumTwo = function (i) {
      // when references 'i' in this function, 'i' goes to the global scope.
      return i;
    };
    // set the value 3 for 'i' in the global scope
    i = 3;
}
console.log(printNumTwo()); // return 3;

try this

"use strict";
var printNumTwo;

for (let i = 0; i < 3; i++) { 
    printNumTwo = function (i) {
      return i;
    }.bind(null, i); // you set the current value as parameter = 0
    i = 3; // i = 3 and break loop
}
console.log(printNumTwo()); // return 0;

try this

"use strict";
var printNumTwo;

for (let i = 0; i < 3; i++) { 
    let i = 0;
    i = 1;
    i = 2;
    printNumTwo = function (i) {
      return i;
    }.bind(null, i); // you set the current value as parameter = 2
    i = 3; // i = 3 and break loop
}
console.log(printNumTwo()); // return 2;
Luis Vasquez
  • 428
  • 3
  • 10
0

I appreciate all the answers I got to my question. All pointing to the case of how a function, when called, handles the environments in which it was both called and created. I read this useful explanation in the book "Eloquent JavaScript" and think it would be good to share it,

"A good mental model is to think of function values as containing both the code in their body and the environment in which they are created. When called, the function body sees the environment in which it was created, not the environment in which it is called." ~ Eloquent_JavaScript/Closure