0

Consider this

fs=[];
for(var i = 0; i < 3; i++){
  fs.push(function(){
    return i;
  });
}
console.log(fs[0](),fs[1](),fs[2]())

I thought that i would be in the closure of the function so that this would print "0 1 2", but nope... it prints "3 3 3".

Why doesn't this work? And is there any way to make this work?

JnBrymn
  • 24,245
  • 28
  • 105
  • 147
  • The fact that it returns "3 3 3" shows that *i* **is** held in a closure. Every function pushed into the array has a closure to the same *i*, so they all show the same value, which is the last value assigned to *i* (i.e. 3). – RobG Feb 12 '14 at 23:53
  • I could [replace var with let](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let). But let appears to not be widely adopted. – JnBrymn Feb 13 '14 at 03:08

2 Answers2

3

Javascript doesn't have a notion of block scope, it only has function scope. Hence the placement of the var i is meaningless in terms of lifetime. It may as well have been the first line in the method.

In order to fix this you need to declare a new scope that holds a separate i value

fs=[];
var makeFunc = function(j) { return function() { return j; } }
for(var i = 0; i < 3; i++){
  fs.push(makeFunc(i));
}
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • yepperino ... makes sense now. Thanks! – JnBrymn Feb 12 '14 at 23:44
  • 1
    Quibbling perhaps, but not so much *declare* a new scope as *create* a new instance of `i`, which requires creating a new execution context. – RobG Feb 12 '14 at 23:57
3

You are actually pushing three instances of the same anonymous function into your 'fs' array. Then your log statement calls that function three times. At the time you are calling it, the loop has completed and the value of 'i' within the closure is 3, so that's what is returned.

I'm not sure what you mean by "work", but maybe something like this is what you mean:

fs = [];

function closureFunc(myVal) {
  return function () {
    return myVal;
  }
}

for (var i = 0; i < 3; i++) {
  fs.push(closureFunc(i));
}

console.log(fs[0](), fs[1](), fs[2]());

This creates three separate closures where the function you are pushing has the scope of the declaring function "closureFunc". Within those closures the myVal is set to the value that was passed in.

Geoff Genz
  • 2,006
  • 17
  • 20