/*Test scope problem*/
for(var i=1; i<3; i++){
//declare variables
var no = i;
//verify no
alert('setting '+no);
//timeout to recheck
setTimeout(function(){
alert('test '+no);
}, 500);
}
It alerts "setting 1" and "setting 2" as expected, but after the timeout it outputs "test 2" twice - for some reason the variable "no" is not reset after the first loop...
I've found only an "ugly" workaround:
/*Test scope problem*/
var func=function(no){
//verify no
alert('setting '+no);
//timeout to recheck
setTimeout(function(){
alert('test '+no);
}, 500);
}
for(var i=1; i<3; i++){
func(i);
}
Any ideas on how to workaround this problem in a more direct way? or is this the only way?
JavaScript does not have block scope, and variable declarations are hoisted. These facts together mean that your code is equivalent to:
var no;
/*Test scope problem*/
for(var i=1; i<3; i++){
//declare variables
no = i;
//verify no
alert('setting '+no);
//timeout to recheck
setTimeout(function(){
alert('test '+no);
}, 500);
}
By the time your timeout function executes, the loop is long finished, with no
retaining its final value of 2.
A way around this would be to pass the current value of no
into a function that creates a fresh callback for each call to setTimeout
. Creating a new function each time means each setTimeout callback is bound to a different execution context with its own set of variables.
var no;
/*Test scope problem*/
for(var i=1; i<3; i++){
//declare variables
no = i;
//verify no
alert('setting '+no);
//timeout to recheck
setTimeout( (function(num) {
return function() {
alert('test '+num);
};
})(no), 500);
}
This is essentially the same as your fix, but using a different syntax to achieve the scoping adjustment.
/*Test scope problem*/
for (var i = 1; i < 3; i++) {
//declare variables
var no = i;
//verify no
alert('setting ' + no);
//timeout to recheck
(function() {
var n = no;
setTimeout(function() {
alert('test ' + n);
}, 500);
})();
}
Javascript does not have lexical scoping(the for-loop does not create a new scope), and your solution is the standard workaround. Another way to write this might be:
[1, 2].forEach(function(no){
//verify no
alert('setting '+no);
//timeout to recheck
setTimeout(function(){
alert('test '+no);
}, 500);
})
forEach() was introduce in ECMAScript 5 and is present in modern browsers but not IE. You can use my library to emulate it though.
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