Reputation: 545975
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));
}
It is also apparent in for in
and for of
loops:
const arr = [1,2,3];
const fns = [];
for (var i in arr){
fns.push(() => console.log("index:", i));
}
for (var v of arr){
fns.push(() => console.log("value:", v));
}
for (const n of arr) {
var obj = { number: n }; // or new MyLibObject({ ... })
fns.push(() => console.log("n:", n, "|", "obj:", JSON.stringify(obj)));
}
for(var f of fns){
f();
}
What’s the solution to this basic problem?
Upvotes: 3261
Views: 486853
Reputation: 192
Already many valid answers to this question. Not many using a functional approach though. Here is an alternative solution using the forEach
method, which works well with callbacks and closures:
let arr = [1,2,3];
let myFunc = (val, index) => {
console.log('val: '+val+'\nindex: '+index);
};
arr.forEach(myFunc);
Upvotes: -1
Reputation: 191
just replaced var
with let
var funcs = []
// change `var` to `let`
for (let i = 0; i < 3; i++) {
funcs[i] = function(){
console.log("My value:", i); //change to the copy
}
}
for (var j = 0; j < 3; j++) {
funcs[j]()
}
Upvotes: 2
Reputation: 41
Let's say you don't use ES6; You can use IIFEs:
var funcs = [];
for (var i = 0; i < 13; i++) {
funcs[i] = (function(x) {
console.log("My value: " + i)
})(i);
}
But it will be different.
Upvotes: 0
Reputation: 1992
We will check , what actually happens when you declare
var
andlet
one by one.
var
<script>
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function () {
debugger;
console.log("My value: " + i);
};
}
console.log(funcs);
</script>
Now open your chrome console window by pressing F12 and refresh the page.
Expend every 3 functions inside the array.You will see an property called [[Scopes]]
.Expand that one. You will see one
array object called "Global"
,expand that one. You will find a property 'i'
declared into the object which having value 3.
Conclusion:
'var'
outside a function ,it becomes global variable(you can check by typing i
or
window.i
in console window.It will return 3).console.log("My value: " + i)
takes the value from its Global
object and display the
result.Now replace the 'var'
with 'let'
<script>
var funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function () {
debugger;
console.log("My value: " + i);
};
}
console.log(funcs);
</script>
Do the same thing, Go to the scopes . Now you will see two objects "Block"
and "Global"
. Now expand Block
object , you
will see 'i' is defined there , and the strange thing is that , for every functions , the value if i
is different (0 , 1, 2).
Conclusion:
When you declare variable using 'let'
even outside the function but inside the loop , this variable will not be a Global
variable , it will become a Block
level variable which is only available for the same function only.That is the reason , we
are getting value of i
different for each function when we invoke the functions.
For more detail about how closer works , please go through the awesome video tutorial https://youtu.be/71AtaJpJHw0
Upvotes: 12
Reputation: 370579
If you're having this sort of problem with a while
loop, rather than a for
loop, for example:
var i = 0;
while (i < 5) {
setTimeout(function() {
console.log(i);
}, i * 1000);
i++;
}
The technique to close over the current value is a bit different. Declare a block-scoped variable with const
inside the while
block, and assign the current i
to it. Then, wherever the variable is being used asynchronously, replace i
with the new block-scoped variable:
var i = 0;
while (i < 5) {
const thisIterationI = i;
setTimeout(function() {
console.log(thisIterationI);
}, i * 1000);
i++;
}
For older browsers that don't support block-scoped variables, you can use an IIFE called with i
:
var i = 0;
while (i < 5) {
(function(innerI) {
setTimeout(function() {
console.log(innerI);
}, innerI * 1000);
})(i);
i++;
}
If the asynchronous action to be invoked happens to be setTimeout
like the above, you can also call setTimeout
with a third parameter to indicate the argument to call the passed function with:
var i = 0;
while (i < 5) {
setTimeout(
(thisIterationI) => { // Callback
console.log(thisIterationI);
},
i * 1000, // Delay
i // Gets passed to the callback; becomes thisIterationI
);
i++;
}
Upvotes: 7
Reputation: 90483
Well, the problem is that the variable i
, within each of your anonymous functions, is bound to the same variable outside of the function.
let
ECMAScript 6 (ES6) introduces new let
and const
keywords that are scoped differently than var
-based variables. For example, in a loop with a let
-based index, each iteration through the loop will have a new variable i
with loop scope, so your code would work as you expect. There are many resources, but I'd recommend 2ality's block-scoping post as a great source of information.
for (let i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
Beware, though, that IE9-IE11 and Edge prior to Edge 14 support let
but get the above wrong (they don't create a new i
each time, so all the functions above would log 3 like they would if we used var
). Edge 14 finally gets it right.
With the relatively widespread availability of the Array.prototype.forEach
function (in 2015), it's worth noting that in those situations involving iteration primarily over an array of values, .forEach()
provides a clean, natural way to get a distinct closure for every iteration. That is, assuming you've got some sort of array containing values (DOM references, objects, whatever), and the problem arises of setting up callbacks specific to each element, you can do this:
var someArray = [ /* whatever */ ];
// ...
someArray.forEach(function(arrayElement) {
// ... code code code for this one element
someAsynchronousFunction(arrayElement, function() {
arrayElement.doSomething();
});
});
The idea is that each invocation of the callback function used with the .forEach
loop will be its own closure. The parameter passed in to that handler is the array element specific to that particular step of the iteration. If it's used in an asynchronous callback, it won't collide with any of the other callbacks established at other steps of the iteration.
If you happen to be working in jQuery, the $.each()
function gives you a similar capability.
What you want to do is bind the variable within each function to a separate, unchanging value outside of the function:
var funcs = [];
function createfunc(i) {
return function() {
console.log("My value: " + i);
};
}
for (var i = 0; i < 3; i++) {
funcs[i] = createfunc(i);
}
for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
Since there is no block scope in JavaScript - only function scope - by wrapping the function creation in a new function, you ensure that the value of "i" remains as you intended.
Upvotes: 2405
Reputation:
This describes the common mistake with using closures in JavaScript.
Consider:
function makeCounter()
{
var obj = {counter: 0};
return {
inc: function(){obj.counter ++;},
get: function(){return obj.counter;}
};
}
counter1 = makeCounter();
counter2 = makeCounter();
counter1.inc();
alert(counter1.get()); // returns 1
alert(counter2.get()); // returns 0
For each time makeCounter
is invoked, {counter: 0}
results in a new object being created. Also, a new copy of obj
is created as well to reference the new object. Thus, counter1
and counter2
are independent of each other.
Using a closure in a loop is tricky.
Consider:
var counters = [];
function makeCounters(num)
{
for (var i = 0; i < num; i++)
{
var obj = {counter: 0};
counters[i] = {
inc: function(){obj.counter++;},
get: function(){return obj.counter;}
};
}
}
makeCounters(2);
counters[0].inc();
alert(counters[0].get()); // returns 1
alert(counters[1].get()); // returns 1
Notice that counters[0]
and counters[1]
are not independent. In fact, they operate on the same obj
!
This is because there is only one copy of obj
shared across all iterations of the loop, perhaps for performance reasons.
Even though {counter: 0}
creates a new object in each iteration, the same copy of obj
will just get updated with a
reference to the newest object.
Solution is to use another helper function:
function makeHelper(obj)
{
return {
inc: function(){obj.counter++;},
get: function(){return obj.counter;}
};
}
function makeCounters(num)
{
for (var i = 0; i < num; i++)
{
var obj = {counter: 0};
counters[i] = makeHelper(obj);
}
}
This works because local variables in the function scope directly, as well as function argument variables, are allocated new copies upon entry.
Upvotes: 64
Reputation: 3343
Here's a simple solution that uses forEach
(works back to IE9):
var funcs = [];
[0,1,2].forEach(function(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
}
Prints:
My value: 0 My value: 1 My value: 2
Upvotes: 35
Reputation: 3331
Till ES5, This problem can only be solved using closure.
But now in ES6, we have block level scope variables. Changing var to let in first for loop will solve the problem.
var funcs = [];
for (let 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
}
Upvotes: 7
Reputation: 25718
With ES6 now widely supported, the best answer to this question has changed. ES6 provides the let
and const
keywords for this exact circumstance. Instead of messing around with closures, we can just use let
to set a loop scope variable like this:
var funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
val
will then point to an object that is specific to that particular turn of the loop, and will return the correct value without the additional closure notation. This obviously significantly simplifies this problem.
const
is similar to let
with the additional restriction that the variable name can't be rebound to a new reference after initial assignment.
Browser support is now here for those targeting the latest versions of browsers. const
/let
are currently supported in the latest Firefox, Safari, Edge and Chrome. It also is supported in Node, and you can use it anywhere by taking advantage of build tools like Babel. You can see a working example here: http://jsfiddle.net/ben336/rbU4t/2/
Docs here:
Beware, though, that IE9-IE11 and Edge prior to Edge 14 support let
but get the above wrong (they don't create a new i
each time, so all the functions above would log 3 like they would if we used var
). Edge 14 finally gets it right.
Upvotes: 169
Reputation: 71810
Try:
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = (function(index) {
return function() {
console.log("My value: " + index);
};
}(i));
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Edit (2014):
Personally I think @Aust's more recent answer about using .bind
is the best way to do this kind of thing now. There's also lo-dash/underscore's _.partial
when you don't need or want to mess with bind
's thisArg
.
Upvotes: 423
Reputation: 5788
Using an Immediately-Invoked Function Expression, the simplest and most readable way to enclose an index variable:
for (var i = 0; i < 3; i++) {
(function(index) {
console.log('iterator: ' + index);
//now you can also loop an ajax call here
//without losing track of the iterator value: $.ajax({});
})(i);
}
This sends the iterator i
into the anonymous function of which we define as index
. This creates a closure, where the variable i
gets saved for later use in any asynchronous functionality within the IIFE.
Upvotes: 295
Reputation: 50010
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:
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = (function() {
var index = i;
return function() {
console.log("My value: " + index);
}
})();
}
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.
Upvotes: 66
Reputation: 36027
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.
var funcs = {};
for (var i = 0; i < 3; i++) {
let index = i; //add this
funcs[i] = function() {
console.log("My value: " + index); //change to the copy
};
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Upvotes: 80
Reputation: 4285
With the support of ES6
, the best way to this is to use let
and const
keywords for this exact circumstance. So var
variable get hoisted
and with the end of loop the value of i
gets updated for all the closures
..., we can just use let
to set a loop scope variable like this:
var funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
Upvotes: -1
Reputation: 178
Use let(blocked-scope) instead of var.
var funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Upvotes: 4
Reputation: 1119
asyncIterable = [1,2,3,4,5,6,7,8];
(async function() {
for await (let num of asyncIterable) {
console.log(num);
}
})();
Upvotes: 0
Reputation: 8178
JavaScript functions "close over" the scope they have access to upon declaration, and retain access to that scope even as variables in that scope change.
var funcs = []
for (var i = 0; i < 3; i += 1) {
funcs[i] = function () {
console.log(i)
}
}
for (var k = 0; k < 3; k += 1) {
funcs[k]()
}
Each function in the array above closes over the global scope (global, simply because that happens to be the scope they're declared in).
Later those functions are invoked logging the most current value of i
in the global scope. That's the magic, and frustration, of closure.
"JavaScript Functions close over the scope they are declared in, and retain access to that scope even as variable values inside of that scope change."
Using let
instead of var
solves this by creating a new scope each time the for
loop runs, creating a separated scope for each function to close over. Various other techniques do the same thing with extra functions.
var funcs = []
for (let i = 0; i < 3; i += 1) {
funcs[i] = function () {
console.log(i)
}
}
for (var k = 0; k < 3; k += 1) {
funcs[k]()
}
(let
makes variables block scoped. Blocks are denoted by curly braces, but in the case of the for loop the initialization variable, i
in our case, is considered to be declared in the braces.)
Upvotes: 25
Reputation: 1618
And yet another solution: instead of creating another loop, just bind the this
to the return function.
var funcs = [];
function createFunc(i) {
return function() {
console.log('My value: ' + i); //log value of i.
}.call(this);
}
for (var i = 1; i <= 5; i++) { //5 functions
funcs[i] = createFunc(i); // call createFunc() i=5 times
}
By binding this, solves the problem as well.
Upvotes: 4
Reputation: 1080
Just change the var keyword to let.
var is function scoped.
let is block scoped.
When you start you code the for loop will iterate and assign the value of i to 3, which will remain 3 throughout your code. I suggest you to read more about scopes in node (let,var,const and others)
funcs = [];
for (let i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] =async function() { // and store them in funcs
await 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
}
Upvotes: 2
Reputation: 2673
This proves how ugly javascript is with regard to how 'closure' and 'non-closure' works.
In the case of:
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] is a global function, and 'console.log("My value: " + i);' is printing global variable i
In the case of
var funcs = [];
function createfunc(i) {
return function() { console.log("My value: " + i); };
}
for (var i = 0; i < 3; i++) {
funcs[i] = createfunc(i);
}
because of this twisted closure design of javascript, 'console.log("My value: " + i);' is printing the i from outer function 'createfunc(i)'
all because javascript can not design something decent like the 'static' variable inside a function like what C programming language is doing!
Upvotes: 0
Reputation: 3196
var funcs = [];
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = function(param) { // and store them in funcs
console.log("My value: " + param); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](j); // and now let's run each one to see with j
}
Upvotes: 3
Reputation: 651
Why not simply call each function inside the first (and only) loop just after they were created, such as:
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]();// and now let's run each one to see
}
Upvotes: -1
Reputation: 1467
Ok. I read through all of the answers. Even though there is a good explanation here - I just could not get this to work. So I went looking on the internet. The person at https://dzone.com/articles/why-does-javascript-loop-only-use-last-value had an answer which is not presented here. So I thought I'd post a short example. This made a lot more sense to me.
The long and short of it is that the LET command is nice but is only now being used. HOWEVER, the LET command is really just a TRY-CATCH combo. This works all the way back to IE3 (I believe). Using the TRY-CATCH combo - life is simple and good. Probably why the EMCScript people decided to use it. It also does not need a setTimeout() function. So no time is lost. Basically, you need one TRY-CATCH combo per FOR loop. Here is an example:
for( var i in myArray ){
try{ throw i }
catch(ii){
// Do whatever it is you want to do with ii
}
}
If you have more than one FOR loop, you just put a TRY-CATCH combo for each one. Also, personally, I always use the double letter of whatever FOR variable I am using. So "ii" for "i" and so on. I am using this technique in a routine to send mouseover commands to a different routine.
Upvotes: 0
Reputation: 7921
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?
//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};
var funcs = {};
for (var i = 0; i < 3; i++) {
var ilocal = i; //create a new local variable
funcs[i] = function() {
console.log("My value: " + ilocal); //each should reference its own local variable
};
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Just like before, where each inner function outputted the last value assigned to i
, now each inner function just outputs the last value assigned to ilocal
. But shouldn't each iteration have it's own ilocal
?
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:
Important: JavaScript does not have block scope. Variables introduced with a block are scoped to the containing function or script, and the effects of setting them persist beyond the block itself. In other words, block statements do not introduce a scope. Although "standalone" blocks are valid syntax, you do not want to use standalone blocks in JavaScript, because they don't do what you think they do, if you think they do anything like such blocks in C or Java.
Reiterated for emphasis:
JavaScript does not have block scope. Variables introduced with a block are scoped to the containing function or script
We can see this by checking ilocal
before we declare it in each iteration:
//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};
var funcs = {};
for (var i = 0; i < 3; i++) {
console.log(ilocal);
var ilocal = i;
}
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 uses ilocal
(more explanation below):
//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};
var funcs = {};
for (var i = 0; i < 3; i++) {
funcs[i] = (function() { //create a new scope using a wrapper function
var ilocal = i; //capture i into a local var
return function() { //return the inner function
console.log("My value: " + ilocal);
};
})(); //remember to run the wrapper function
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
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:
//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};
var funcs = {};
for (var i = 0; i < 3; i++) {
funcs[i] = wrapper(i);
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
//creates a separate environment for the inner function
function wrapper(ilocal) {
return function() { //return the inner function
console.log("My value: " + ilocal);
};
}
Update
With ES6 now mainstream, we can now use the new let
keyword to create block-scoped variables:
//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};
var funcs = {};
for (let i = 0; i < 3; i++) { // use "let" to declare "i"
funcs[i] = function() {
console.log("My value: " + i); //each should reference its own local variable
};
}
for (var j = 0; j < 3; j++) { // we can use "var" here without issue
funcs[j]();
}
Look how easy it is now! For more information see this answer, which my info is based off of.
Upvotes: 184
Reputation: 725
While this question is old and answered, I have yet another fairly interesting solution:
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
for (var i = 0; i < 3; i++) {
funcs[i]();
}
The change is so small it's almost difficult to see what I did. I switched the second iterator from a j to an i. This somehow refreshes the state of i in time to give you the desired result. I did this by accident but it makes sense considering previous answers.
I wrote this up to point out this small, yet very important difference. Hope that helps to clear up some confusion for other learners like me.
Note: I am not sharing this because I think it's the right answer. This is a flakey solution that probably will break under certain circumstances. Actually, I'm quite amazed that it really works.
Upvotes: 0
Reputation: 11602
Another way that hasn't been mentioned yet is the use of Function.prototype.bind
var funcs = {};
for (var i = 0; i < 3; i++) {
funcs[i] = function(x) {
console.log('My value: ' + x);
}.bind(this, i);
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
UPDATE
As pointed out by @squint and @mekdev, you get better performance by creating the function outside the loop first and then binding the results within the loop.
function log(x) {
console.log('My value: ' + x);
}
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = log.bind(this, i);
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Upvotes: 392
Reputation: 3376
This question really shows the history of JavaScript! Now we can avoid block scoping with arrow functions and handle loops directly from DOM nodes using Object methods.
const funcs = [1, 2, 3].map(i => () => console.log(i));
funcs.map(fn => fn())
const buttons = document.getElementsByTagName("button");
Object
.keys(buttons)
.map(i => buttons[i].addEventListener('click', () => console.log(i)));
<button>0</button><br>
<button>1</button><br>
<button>2</button>
Upvotes: 10
Reputation: 2763
The most simple solution would be,
Instead of using:
var funcs = [];
for(var i =0; i<3; i++){
funcs[i] = function(){
alert(i);
}
}
for(var j =0; j<3; j++){
funcs[j]();
}
which alerts "2", for 3 times. This is because anonymous functions created in for loop, shares same closure, and in that closure, the value of i
is the same. Use this to prevent shared closure:
var funcs = [];
for(var new_i =0; new_i<3; new_i++){
(function(i){
funcs[i] = function(){
alert(i);
}
})(new_i);
}
for(var j =0; j<3; j++){
funcs[j]();
}
The idea behind this is, encapsulating the entire body of the for loop with an IIFE (Immediately-Invoked Function Expression) and passing new_i
as a parameter and capturing it as i
. Since the anonymous function is executed immediately, the i
value is different for each function defined inside the anonymous function.
This solution seems to fit any such problem since it will require minimal changes to the original code suffering from this issue. In fact, this is by design, it should not be an issue at all!
Upvotes: 53
Reputation: 10849
This is a problem often encountered with asynchronous code, the variable i
is mutable and at the time at which the function call is made the code using i
will be executed and i
will have mutated to its last value, thus meaning all functions created within the loop will create a closure and i
will be equal to 3 (the upper bound + 1 of the for
loop.
A workaround to this, is to create a function that will hold the value of i
for each iteration and force a copy i
(as it is a primitive, think of it as a snapshot if it helps you).
Upvotes: 2