I have two blocks of code which I believe should produce the same result:
1.
for(var i=0;i<10;i+=1){
var j=i;
setTimeout(function(){
console.log(j);
},100);
}
2.
for(var i=0;i<10;i+=1){
(function(j){
setTimeout(function(){
console.log(j);
},100);
})(i);
}
However, as most of you might expect, the first one log 9 ten times, and the second one log correctly from 0 to 9.
The second one is using closure to preserve the value of i. I think the first one should preserve the value as well, because:
var j
create a new variable j in each iteration.i
value is assigned to this new j
in the iteration.j
is then bound to the setTimeout
's function in the same iteration.But it turns out that the j
is bound to the last i
value for all iteration.
So, whats the differences between creating a variable using function argument and var
?
Please point out any mistake ! Thanks in advance !
Thank you all ! I did not know that javascript only has function and global scope ! Blame the other languages who taught me to do so :P
The second one is using closure to preserve the value of i.
In fact, both results you're seeing are as a result of how closures work. Both of the functions you're passing into setTimeout
are closures.
I think the first one should preserve the value as well, because:
the var j create a new variable j in each iteration.
No, it doesn't. There's only one j
in your first example. In JavaScript (for now), variables only have function or global scope, never block scope. What the JavaScript actually does with your first example looks much more like this:
var i;
var j;
for(i=0;i<10;i+=1){
j=i;
setTimeout(function(){
console.log(j);
},100);
}
The resulting closure has an enduring reference to that one j
variable, which is why you get the same value over and over.
Your second example is a bit of an anti-pattern, because it's both hard to read, and it's needlessly creating and throwing away functions on every loop. Let's make that more obvious by adding some variables; this code does exactly the same thing as your second example, the only change is the intermediate variables I've added:
for(var i=0;i<10;i+=1){
var f1 = function(j){
var f2 = function(){
console.log(j);
};
setTimeout(f2,100);
};
f1(i);
}
Instead:
for(var i=0;i<10;i+=1){
setTimeout(makeHandler(i));
}
function makeHandler(j){
return function(){
console.log(j);
};
}
Both easier to read and it avoids re-creating the makeHandler
function on each loop.
More to explore (on my blog):
var
In your first code sample j
is not bound the value of i
in that iteration of the loop. This is because you can't declare variables in a loop. Loops have no scope in JavaScript, only functions have scope.
The first code sample is equivalent to:
var i, j; // all variable declarations are hoisted to the top
for (i = 0; i < 10; i += 1) {
j = i;
setTimeout(function() {
console.log(j);
}, 100);
}
Since in the second code sample j
is declared in the anonymous-immediate function it is bound to the value of i
in that iteration because of a pass-by-value trick. This is called closing the scope or simply a closure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With