3

In continuation to this question, I tried the following code that uses the same variable in both the loops and I get the desired result. My question is, WHY ?

So the initial code is:

 var funcs = [];

 for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = function() {          // and store them in funcs
    console.log("My value: " + i); // each should log its value.
  };
 }

 for (var j = 0; j < 3; j++) {
  funcs[j]();                      // this will not give desired output
 }

The outputs this:

  • My value: 3
  • My value: 3
  • My value: 3

Whereas, the intended output is:

  • My value: 0
  • My value: 1
  • My value: 2

Now, if I use variable 'i' (a global variable) in the second loop as well, the code looks like:

var funcs = [];

for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = function() {          // and store them in funcs
    console.log("My value: " + i); // each should log its value.
  };
}

for ( i = 0; i < 3; i++) {
  funcs[i]();                      // this gives desired output
}

I get the intended output,

  • My value: 0
  • My value: 1
  • My value: 2

WHY?

Community
  • 1
  • 1
Prakash Tiwari
  • 2,351
  • 1
  • 20
  • 13
  • 2
    because `i` is a global in your code – Jaromanda X Jan 29 '17 at 11:36
  • 3 it's last value for i, when you calling it – L. Vadim Jan 29 '17 at 11:37
  • rewrite your code to declare `var i` once, before the for loops - because that is what you are actually doing, the two `var i` are actually the same `i` - it becomes clear – Jaromanda X Jan 29 '17 at 11:38
  • The whole point of the "closures inside loops" issue is that the inner function prints the *current* value of `i`. If you change the second loop to e.g. `for (var i = 10; i < 13; i++) {` it should become more clear. – JJJ Jan 29 '17 at 11:39
  • Even if you put `var i` it still global (at least belongs to the scope outside the `for`s). Try wrap each `for` inside a function then you'll get the result of 3 `3`s no matter what variable you use (even `i` itself). – ibrahim mahrir Jan 29 '17 at 11:50

2 Answers2

2

Now, if I use variable 'i' (a global variable) in the second loop as well ... I get the intended output ... WHY?

Because when you do the first executing loop:

for (var j = 0; j < 3; j++) {
  funcs[j]();                      
 }

the loop constructing the array has finished and the global variable i has the value 3 and therefore console.log(i) results in logging 3 to the console for each iteration of the first executing loop. When you do the second executing loop:

for ( i = 0; i < 3; i++) {
  funcs[i]();                      
}

you for each iteration of the loop assign a new value to the global variable i and it is this new value that the console.log(i) will log to the console: 0, 1, 2.

How do you achieve the desired result in a simple way?

You can use let to achieve the desired result in a simple way:

"use strict"; // use of let requires strict mode

var funcs = [];

 for (let i = 0; i < 3; i++) { // just replace var with let here     
  funcs[i] = function() {          
    console.log("My value: " + i); 
  };
 }

 for (var j = 0; j < 3; j++) {
  funcs[j]();                     
 }

Now the array will be constructed with functions having access to a local variable, which is assigned the value of i at the time of adding the function to the array.

rabbitco
  • 2,790
  • 3
  • 16
  • 38
1

Because the variable i is contaminated. In your snippets, The loops write to the same variable i and the functions reads it. Note that there is just one variable i in your code.

What you really need to get your intended result is called "module pattern", which wraps a function to keep a local copy of the variable in the loop:

for (var i=0; i<3; i++) {
    (function (j) {
        funcs[j] = function() {          // and store them in funcs
            console.log("My value: " + j); // each should log its value.
        };
    })(i);
}

You can read this article to get more information about "module pattern".

shuangwhywhy
  • 5,475
  • 2
  • 18
  • 28