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?
After reading through various solutions, I'd like to add that the reason those solutions work is to rely on the concept of scope chain. It's the way JavaScript resolve a variable during execution.
var
and itsarguments
.window
.In the initial code:
When
funcs
gets executed, the scope chain will befunction inner -> global
. Since the variablei
cannot be found infunction inner
(neither declared usingvar
nor passed as arguments), it continues to search, until the value ofi
is eventually found in the global scope which iswindow.i
.By wrapping it in an outer function either explicitly define a helper function like harto did or use an anonymous function like Bjorn did:
When
funcs
gets executed, now the scope chain will befunction inner -> function outer
. This timei
can be found in the outer function's scope which is executed 3 times in the for loop, each time has valuei
bound correctly. It won't use the value ofwindow.i
when inner executed.More detail can be found here
It includes the common mistake in creating closure in the loop as what we have here, as well as why we need closure and the performance consideration.
try this shorter one
no array
no extra for loop
http://jsfiddle.net/7P6EN/
Your code doesn't work, because what it does is:
Now the question is, what is the value of variable
i
when the function is called? Because the first loop is created with the condition ofi < 3
, it stops immediately when the condition is false, so it isi = 3
.You need to understand that, in time when your functions are created, none of their code is executed, it is only saved for later. And so when they are called later, the interpreter executes them and asks: "What is the current value of
i
?"So, your goal is to first save the value of
i
to function and only after that save the function tofuncs
. This could be done for example this way:This way, each function will have it's own variable
x
and we set thisx
to the value ofi
in each iteration.This is only one of the multiple ways to solve this problem.
Use closure structure, this would reduce your extra for loop. You can do it in a single for loop:
Here's another variation on the technique, similar to Bjorn's (apphacker), which lets you assign the variable value inside the function rather than passing it as a parameter, which might be clearer sometimes:
Note that whatever technique you use, the
index
variable becomes a sort of static variable, bound to the returned copy of the inner function. I.e., changes to its value are preserved between calls. It can be very handy.The reason your original example did not work is that all the closures you created in the loop referenced the same frame. In effect, having 3 methods on one object with only a single
i
variable. They all printed out the same value.