0

I found many topics explaining this problem, on how I can fix the following code by using var, like this one http://conceptf1.blogspot.com/2013/11/javascript-closures.html or this one JavaScript closure inside loops – simple practical example.

But I really can't understand why it is not working when using var and working when using let.

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]();                        //  and now let's run each one to see
}
// outputs 3 3 3

I really have no clue...

user10089632
  • 5,216
  • 1
  • 26
  • 34
Marek
  • 3,935
  • 10
  • 46
  • 70
  • Do you mean "*not working when using var and working when using let*"? – user10089632 Nov 19 '17 at 00:16
  • 2
    Possible duplicate of [Explanation of \`let\` and block scoping with for loops](https://stackoverflow.com/questions/30899612/explanation-of-let-and-block-scoping-with-for-loops) – user10089632 Nov 19 '17 at 00:30
  • I checked all the other duplicates and I couldn't understand them, only the answer below helped me to understand this problem. – Marek Nov 19 '17 at 01:04

3 Answers3

1

ES6's let is block scope that means it has it's own scope inside {} like many other traditional languages. But in contrast var is a global variable in your code.

In first for loop, function is just assigned to func[i] 3 times with final value 3 but not executed. If you execute the function inside first loop, you will get the expected output though 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.
  };
  funcs[i](); // execution of func
}

So, the important thing is in which context your function is executing.

Now, by the time funcs[j]() is executed for the first time in your code, i's value is already 3. If you want to log incremented value, you have to pass that as an argument like the following:

var funcs = [];
for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = function(j) {          // and store them in funcs
    console.log("My value: " + j); // each should log its value.
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j](j);                      // and now let's run each one to see
}
Mamun
  • 66,969
  • 9
  • 47
  • 59
  • I think I start to understand it now. So this line of code -> console.log("My value: " + i); will use the global i (3) value when using var? And if I use let, each iteration will maintain different i value for the function definition? – Marek Nov 19 '17 at 00:53
  • So basically, 3 iterations will "make" 3 block scopes? – Marek Nov 19 '17 at 00:59
  • I think that is the best explanation I have found so far. Thanks – Marek Nov 19 '17 at 01:03
0

Because var is function-scoped (that is, having the scope of the surrounding function), whereas let & const are block-scoped - thus having their own values inside each block (be it if- or else-block or each iteration of the loop as in your case). Block-scoped variables are not defined outside the block. Function-scoped variables stay until the end of the function.

Your i variable is function-scoped, which means once your loop is completed, it's still present outside the first loop & has a value of 3. So once you call your array function, it takes i from the upper scope (which is 3) & outputs it. If you used let, for example, you'd get a fresh binding for each iteration & your i's value would keep the initial declaration value.

Edit: So in order to output sequential values, you need to replace var with let:

for (let i = 0; i < 3; i++) {
  funcs[i] = function() {
    // now `i` is bound to the scope & keeps its initial value
    console.log("My value: " + i); 
  };
}
inker
  • 325
  • 8
  • 15
  • could you add rewritten code to my answer, so I can easily understand what is going on? Is it something like: var i = 0; for (i; i < 3; i++) ? Could you write when i scope start and finish when using var and let? – Marek Nov 19 '17 at 00:39
  • I know how to make it work, all other posts and topics gave me an answer, but I dont really see why the initial solution with var is not working... Maybe there exist some other code example that I can understand? – Marek Nov 19 '17 at 00:46
  • 1
    @Marek, if your question is "*but I dont really see why the initial solution with var is not working*" then you should say so, and you should not iclude `let` in there because that confuses by opening another questions, I invite you to edit your post to reflect what you really wonder about – user10089632 Nov 19 '17 at 00:49
0

Unlike to let, var is hoisted outside the loop scope. In fact, your i variable will always equals to the last iteration (which in your example is 3). let doesn’t have this problem cause it is not hoisted.

Zacol
  • 93
  • 5