This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 9 years ago.
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?
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.
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));
}
Related
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
How do JavaScript closures work?
(86 answers)
Closed 7 years ago.
I am confused why the following code snippet has (i) at the end of it:
for (var i = 0; i < 10; i += 1) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 1000);
})(i);
}
Ive seen it in production code Ive worked on--I just can intuit why it's necessary.
You are defining an inline function, so you could force the i parameter to be in the local scope at the time of the execution of the console.log statement. By adding the parameter, you are creating a self executing function. Similar would be the following statements, which might be better to read...
function logmeWithTimeOut(value) {
setTimeout(function() { console.log(value); }, 1000);
}
for (var i = 0; i < 10; i++) {
logmeWithTimeout(i);
}
Though I might prefer
function logmeWithTimeout(value) {
console.log(value);
}
for (var i = 0; i < 10; i++) {
setTimeout(logmeWithTimeout.bind(undefined, i), 1000);
}
It forces the i to be in local scope, otherwise your log would print only 11
Because you are defining a function inside the parenthesis, and then you are calling it passing i as parameter.
If you didn't put the (i) there, you would just define a function but never call it.
This question already has answers here:
Javascript infamous Loop issue? [duplicate]
(5 answers)
Closed 8 years ago.
My understanding of Javascript is that variables have function scope, not block scope, so variables declared inside a block are hoisted to the function above it. This article gives the following code example:
var arr = [1, 2, 3, 4]
for(var i = 0; i < arr.length; i++) {
var entry = arr[i];
setTimeout(function() {
console.log(entry);
}, 100);
}
The article states the the code gets translated into the following, and so the variable entry is common between each iteration. The result is that 4 is logged to the console four times
var arr, i, len, entry;
arr = [1, 2, 3, 4]
for(i = 0; i < arr.length; i++) {
entry = arr[i];
setTimeout(function() {
console.log(entry);
}, 100);
}
My question is, how should I rewrite the code if I want each call to setTimeout to log a different value from arr?
You can change the setTimeout to:
setTimeout(
(function()
{
var entry = arr[i];
return function() {console.log(entry);}
}
)()
);
That way, you create a new scope, and entry is in that scope.
You can also do:
(function(e)
{
setTimeout(function(){console.log(e);});
}
)(entry);
Which also creates a new scope, in which e lies.
Yet another option:
setTimeout(
(function(e)
{
return function() {console.log(e);};
}
)(entry);
);
In all cases, you use an anonymous function that you call immediately, so the only side-effect is that you are creating a new scope.
(edit: reformated a bit for readability, and added a bit more explanations)
Can anyone explain why document.write part always outputs 10?
function creatFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function () {
return i;
}
}
return result;
}
var funcs = creatFunctions();
for (var i = 0; i < funcs.length; i++) {
document.write(funcs[i]() + "<br />");
}
I think the answer to this question is more thorough than the duplicate question, therefore worth keeping this post.
Sure.
In your for loop, you reference i. What you expect to be happening is that each closure gets a snapshot of i at the time the function is created, therefore in the first function it would return 0, then 1, etc.
What's really happening is that each closure is getting a reference to the external variable i, which keeps updating as you update i in the for loop.
So, the first time through the loop, you get a function that returns i, which at this point is 0. The next time you get two functions which return i, which is now 1, etc.
At the end of the loop, i==10, and each function returns i, so they all return 10.
UPDATE TO ADDRESS QUESTION IN COMMENT:
It's a little confusing since you use i in two different contexts. I'll make a very slight change to your code to help illustrate what's going on:
function creatFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function () {
return i;
}
}
return result;
}
var funcs = creatFunctions();
// NOTE: I changed this `i` to `unrelated_variable`
for (var unrelated_variable = 0; unrelated_variable < funcs.length; unrelated_variable++) {
document.write(funcs[unrelated_variable]() + "<br />");
}
... the functions that you create in your creatFunctions() function all return i. Specifically, they return the i that you create in the for loop.
The other variable, which I've renamed to unrelated_variable, has no impact on the value returned from your closure.
result[i] = function () {
return i;
}
... is not the same thing as result[2] = 2. It means result[2] = the_current_value_of_i
Because you reference i as a variable, which, when the function executes, has the value 10. Instead, you could create a copy of i by wrapping in in a function:
(function (i) {
result[i] = function () {
return i;
}
}(i));
The i that you are returning inside the inner function - is the i that was declared inside the for(var i=0...) therefor - it is the same i for all of the functions in result
and by the time you are calling the functions its value is 10 (after the loop ends)
to accomplish what you wanted you should declare another variable inside the scope of the anonymous function
Because reference to "i" is boxed (enclosed) in your closure that you defined.
result[i] = function () {
return i;
}
And it just keeps reference to the "i" var which keeps changing with each iteration.
UPDATE
This is related to the this keyword function scope which defines variable context.
Hence, in JavaScript, there's no such thing as block scope (for, if else, while, ...).
With that in mind, if you assign var funcs to a function definitioncall, the function body (this) will be bound to the global scope var i inside that function will reach the end of the for loop and stays there in memory. In this case i is 10.
Unfortunately for you, since function scope puts every variable to the top, var i will be global as well.
Although I was completely wrong at first, I stand corrected and took the liberty to create the output of your script in this demo.
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
Here's a simplification of my JS program. mylist is an array of strings and MyAsync is a function that takes an object and arranges for the supplied callback to be called at a later time.
for (var myindex = 0; myindex < mylist.length; myindex += 1) {
MyAsync(mylist[myindex], function () { alert(myindex); });
}
When there's ten items in mylist, the result is two alert messages each showing "10". Okay, it's clearly using the value of myindex after the loop finishes. So I make a slight change...
for (var myindex = 0; myindex < mylist.length; myindex += 1) {
var copyindex = myindex;
MyAsync(mylist[myindex], function () { alert(copyindex); });
}
Now, each alert shows "9".
How do I please arrange for the callback function to know what myindex was at the time MyAsync was invoked?
Asynchronously, billpg.
Yeah, as the comments and not making functions in a loop for the reason you are experiencing.
We can use a recursive function instead :
var len = mylist.length;
function asyncLoop(index) {
MyAsync(mylist[index], function () {
if(index < len) {
asyncLoop(++index);
}
});
}
asyncLoop(0);
As comments , - firing them all off at the same time* ( *- quick as a loop can run ). and keeping track of the array count ...
function asyncDo(index) {
MyAsync(mylist[index], function () {
/* we have access to the array index here */
console.log(index);
});
}
for ( var i=0, len=mylist.length; i<len; ++i) { asyncDo(i); }
You can use closures to do this: here is some code that I think demonstrates what you want, printing out 0 .. 9.
var funcs = [];
for(var i = 0; i < 10; i++){
funcs.push( (function(j){
return function(){
console.log(j);
};
})(i));
}
funcs.forEach(function(f){
f();
});
The idea is that in returning a function to be added to the list, the value of i is captured when that function was constructed from the parameter to the outer function.
Here's an SO post that helped me with this: JavaScript closures vs. anonymous functions
I was going through a javascript book and have encountered the following code on closure
function constFunc() {
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs[i] = function () {
return i;
}
}
return funcs;
}
var f = constFunc();
f[5]();
The output was not 5.
The explanation given was "nested functions do not make private copies of the scope".
i would be grateful if could someone explain the above statement.
When you call constFunc the loop runs and assigns a function to each element of the funcs array. Those functions each return the value of i, which changes on each iteration of the loop from 0 up to 9.
That means that once constFunc has finished executing i will have the value of 9. When you call one of the functions in the array it returns the value of i which as we've just seen is now 9. The value of i is not captured on each iteration of the loop.
You can force it to be captured via a closure in the loop:
for (var i = 0; i < 10; i++) {
(function (i) {
funcs[i] = function () {
return i;
};
}(i));
}
In this case you are effectively creating a new variable with the same value as i on every iteration of the loop. You can see that in action here.
By passing i into a function you end up copying the value of it. So on the first iteration i has the value 0. We pass that value into the new function (the argument i is a different variable to the i declared in the loop initialiser) and then return that value from the array function. This way, each array function is returning a different i, instead of each returning the same i.
WHy did you expect the output of 5? RIght, because at the time the function was created, i was 5. Now, as we can see, the output is not 5 (but, if I didn't miss anything, 9). Now why is that?
The iinside the closure is not evaluated when the function is created, but when it runs. Now this is where the statement you qoted comes into play: there is only ONE i- the one created inside constFunc. All closures work on that one. After constFunc si done working, that i has, obviously, the value of 9 (becaus that's where the loop stops). SO all closures now output 9.
A simpler example:
function outer () {
var i = 0;
function inner () {
return i;
}
i = 99;
}
outer();
inner(); // <- return 99, not 0.
There is one variable called i.
There are 10 functions that return i.
Each one of those functions returns the current value of the single i variable.
Here in this loop:
for(var i=0;i<10;i++)
{
funcs[i]=function(){return i;}
}
due to closure, inside function you are accessing parent level variable of i, which after complete loop will be 10. So after loop when you call these function it will return you value of i which is 10.
You can solve it by passing variable as parameter, as parameter create the new copy of variable which maintain its state even on completion of loop.
var myFunctions= [];
function createMyFunction(i) {
return function() {
console.log("My value: " + i);
};
}
for (var i = 0; i < 3; i++) {
myFunctions[i] = createMyFunction(i);
}