Function inside for loop

hi
can anyone explain why variable “i” updates to 3 inside the function in the third iteration of the for loop i-e when i =2.

var printNumTwo;
for (var i = 0; i < 3; i++) {
  console.log("i1: ",i);
  if(i === 2){
    console.log("i2: ",i);
    printNumTwo = function() {
      console.log("i3: ",i);
      return i;
    };
  }
}
console.log("ans: ",printNumTwo());
// returns 3

see this link please to run the code:
link

thanks all in advance!

1 Like

Generally declaring functions in loops is a problem. Most linters will flag it with a warning.

Your function will always return whatever the current value of i is at the time you invoke it. By using var to declare i it is available globally. Add a line to your code i=23; and your function will return 23.

Why 3? Your i increments by one. When it becomes three the test condition becomes false and your loop ends. At the end of your loop i did equal 3.

If you use let to declare i then you will have created a closure and your function will work as expected. That particular i will not be available globally but will live on after your block goes out of scope because it is used in your function. I would suggest finding a good tutorial or article on closures - it is a thick subject and I’m no expert.

var printNumTwo;
for (var i = 0; i < 3; i++) {
  console.log("i1: ",i);
  if(i === 2){
    console.log("i2: ",i);
    printNumTwo = function() {
      console.log("i3: ",i);
      return i;
    };
  }
}

i = 23;  // read up on polluting global namespace
// we use i a lot in loops so this could be a problem
console.log("ans: ",printNumTwo());
// returns 23  !!!!!!!!!!!!!!!
1 Like

This is due to closures. Essentially in javascript, unlike other languages, functions have access to anything in their scope after they return.

So what happens when you use var is that the variable is bound to the global scope, and every function has access to it. When we finally call printNumTwo, it still can only access the global i at the moment you call it, which will be 3, since the loop ends when it reaches 3.

I find it helps me to walk through how the js engine runs this code, line by line.

  1. assign variable printNumTwo to the global scope (since you’re using var)
  2. enter the for loop and assign the global variable i and set it to 0
  3. run the for loop and console.log every value for i
  4. if i === 2 console.log
  5. if i === 2 assign printNumTwo to the inner function
  6. store a reference to the global variable i inside printNumTwo
  7. exit the loop when the global i === 3
  8. console.log("ans: ",printNumTwo()) the current value of i

At step 8 the the value of the global i is 3, and the reference inside printNumTwo is mutated (changed). So that’s what gets logged.

Using es6 this is easily solved by using let whenever you intend to reassign a variable. let is bound to the function scope that sets it. Therefore step 6 changes and instead does

  • store a reference to the local variable i in the inner function (which is equal to 2 at this point)

Then at step 8, printNumTwo has access to the value of i when it was assigned.

Hope that helps. Closures take a while to understand sometimes.

2 Likes

@alhazen1 @JM-Mendez thank you so much for the needed help and clear elaboration !

1 Like