var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
// and store them in funcs
funcs[i] = function() {
// each should log its value.
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
It outputs this:
My value: 3
My value: 3
My value: 3
Whereas I'd like it to output:
My value: 0
My value: 1
My value: 2
The same problem occurs when the delay in running the function is caused by using event listeners:
var buttons = document.getElementsByTagName("button");
// let's create 3 functions
for (var i = 0; i < buttons.length; i++) {
// as event listeners
buttons[i].addEventListener("click", function() {
// each should log its value.
console.log("My value: " + i);
});
}
<button>0</button>
<br />
<button>1</button>
<br />
<button>2</button>
… or asynchronous code, e.g. using Promises:
// Some async wait function
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));
for (var i = 0; i < 3; i++) {
// Log `i` as soon as each promise resolves.
wait(i * 100).then(() => console.log(i));
}
What’s the solution to this basic problem?
What you need to understand is the scope of the variables in javascript is based on the function. This is an important difference than say c# where you have block scope, and just copying the variable to one inside the for will work.
Wrapping it in a function that evaluates returning the function like apphacker's answer will do the trick, as the variable now has the function scope.
There is also a let keyword instead of var, that would allow using the block scope rule. In that case defining a variable inside the for would do the trick. That said, the let keyword isn't a practical solution because of compatibility.
Use let(blocked-scope) instead of var.
Using an Immediately-Invoked Function Expression, the simplest and most readable way to enclose an index variable:
This sends the iterator
i
into the anonymous function of which we define asindex
. This creates a closure, where the variablei
gets saved for later use in any asynchronous functionality within the IIFE.You could use a declarative module for lists of data such as query-js(*). In these situations I personally find a declarative approach less surprising
You could then use your second loop and get the expected result or you could do
(*) I'm the author of query-js and therefor biased towards using it, so don't take my words as a recommendation for said library only for the declarative approach :)
Bit late to the party, but I was exploring this issue today and noticed that many of the answers don't completely address how Javascript treats scopes, which is essentially what this boils down to.
So as many others mentioned, the problem is that the inner function is referencing the same
i
variable. So why don't we just create a new local variable each iteration, and have the inner function reference that instead?Just like before, where each inner function outputted the last value assigned to
i
, now each inner function just outputs the last value assigned toilocal
. But shouldn't each iteration have it's ownilocal
?Turns out, that's the issue. Each iteration is sharing the same scope, so every iteration after the first is just overwriting
ilocal
. From MDN:Reiterated for emphasis:
We can see this by checking
ilocal
before we declare it in each iteration:This is exactly why this bug is so tricky. Even though you are redeclaring a variable, Javascript won't throw an error, and JSLint won't even throw a warning. This is also why the best way to solve this is to take advantage of closures, which is essentially the idea that in Javascript, inner functions have access to outer variables because inner scopes "enclose" outer scopes.
This also means that inner functions "hold onto" outer variables and keep them alive, even if the outer function returns. To utilize this, we create and call a wrapper function purely to make a new scope, declare
ilocal
in the new scope, and return an inner function that usesilocal
(more explanation below):Creating the inner function inside a wrapper function gives the inner function a private environment that only it can access, a "closure". Thus, every time we call the wrapper function we create a new inner function with it's own separate environment, ensuring that the
ilocal
variables don't collide and overwrite each other. A few minor optimizations gives the final answer that many other SO users gave:Update
With ES6 now mainstream, we can now use the new
let
keyword to create block-scoped variables:Look how easy it is now! For more information see this answer, which my info is based off of.
Another way of saying it is that the
i
in your function is bound at the time of executing the function, not the time of creating the function.When you create the closure,
i
is a reference to the variable defined in the outside scope, not a copy of it as it was when you created the closure. It will be evaluated at the time of execution.Most of the other answers provide ways to work around by creating another variable that won't change the value for you.
Just thought I'd add an explanation for clarity. For a solution, personally, I'd go with Harto's since it is the most self-explanatory way of doing it from the answers here. Any of the code posted will work, but I'd opt for a closure factory over having to write a pile of comments to explain why I'm declaring a new variable(Freddy and 1800's) or have weird embedded closure syntax(apphacker).