views:

83

answers:

5

Running the following code:

for (var i=0; i<3; i++) { 
   setTimeout( function() { console.log(i); } , 500 ); 
}

Outputs "3" three times. It's outputting the final value of i as opposed to the value of i when the inner function is created.

If I want the output to be 1, 2, and 3, how would I write this code? How can I get it to use the value of i at the time the function is defined as opposed to its final value?

+5  A: 
for (var i=0; i<3; i++) {
   setTimeout( function(val) { return function() { console.log(val); } }(i), 500 );
}

So, at setTimeout time (at the time we define the function for setTimeout), we're calling the anonymous function taking val as a parameter. This creates a closure for each function call, storing the value of val within the scope of the function we just called. I used a self-invoking function, which creates an immediate closure.

In the code you provided, the code creates a closure, but for the larger scope of the entirety of the code, so i is local to the whole code, meaning that at run-time, the anonymous function will use the variable i that the rest of the code uses.

palswim
This example uses two anonymous functions, whereas @z5h's answer uses a named function, which may illustrate the concept more clearly.
palswim
+4  A: 
function f(i){
  return function(){console.log(i);};
}

for (var i=0; i<3; i++) { 
   setTimeout( 
     f(i)
   , 500 ); 
}
z5h
+1  A: 

alternative:

for (var i=0; i<3; i++) {
   (function(val){
       setTimeout(function() {
           console.log(val);
       },500)
   }(i));
}
David Murdoch
A: 

you can try generating a static string for the setTimeout function

for (var i=0; i<3; i++) { setTimeout('console.log('+i+');', 500); }

Alex
I would say... **don't** use this method. But if you do, don't get in the habit of relying on strings of JavaScript you construct for execution.
palswim
i know that, i'm just suggesting a solution which might be the way for the questioner
Alex
-1.. this is bad coding style that amounts to using **eval** in a place where it's not necessary
Claudiu
+2  A: 

The modern alternative to an explicit closure (which can get a bit hairy to read when you've got a double-wrapped function) is Function#bind. Once you've hacked in support for browsers that don't do ECMAScript Fifth Edition yet, you can say:

for (var i=0; i<3; i++) {
    setTimeout(function(i) { console.log(i); }.bind(window, i), 500);
}

the window is the value that this will be inside the function (you don't need a this here, so we just use the default global object). In the case where you're just calling another function/method, like here with console.log, you can use that to excise the function expression completely:

for (var i=0; i<3; i++) {
    setTimeout(console.log.bind(console, i), 500);
}
bobince