/*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);
}
它按预期警告“设置1”和“设置2”,但在超时后它输出“test 2”两次 - 由于某种原因,变量“no”在第一次循环后没有重置......
我发现只有一个“丑陋”的解决方法:
/*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);
}
有关如何以更直接的方式解决此问题的任何想法?或者这是唯一的方法吗?
答案 0 :(得分:13)
JavaScript没有块范围,并且提升了变量声明。这些事实共同意味着您的代码等同于:
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);
}
当你的超时函数执行时,循环结束很长时间,no
保持其最终值为2。
解决这个问题的方法是将no
的当前值传递给一个函数,该函数为每次调用setTimeout
创建一个新的回调。每次创建一个新函数意味着每个setTimeout回调都绑定到具有自己的变量集的不同执行上下文。
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);
}
答案 1 :(得分:2)
这与您的修复基本相同,但使用不同的语法来实现范围调整。
/*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);
})();
}
答案 2 :(得分:1)
我喜欢我可以从this answer获得如此多的里程数。
如果你需要帮助来应用这个答案,请告诉我。
不确定。我们来看看你的原始代码。
//timeout to recheck
setTimeout(function(){
alert('test '+no);
}, 500);
看到那个匿名函数?你传入setTimeout()
的那个?在计时器这样说之前不会调用它 - 在循环退出后,500ms 很好。
您希望的是no
“就地”进行评估,但不是 - 它在调用函数时进行评估。到那时,no
是2.
为了解决这个问题,我们需要一个在循环迭代期间执行的函数,它本身将返回setTimeout()
可以按照我们期望的方式使用的函数。
setTimeout(function( value )
{
// 'value' is closed by the function below
return function()
{
alert('test ' + value );
}
}( no ) // Here's the magic
, 500 );
由于我们创建了匿名函数并立即调用它,因此创建了一个新的作用域,我们可以在其中关闭变量。该范围在value
左右,而不是no
。由于value
每个循环都会收到一个新的(ahem)值,因此每个lambdas都有自己的值 - 我们想要的值。
因此,当setTimeout()触发时,它正在执行从闭包函数返回的函数。
我希望能够解释它。
答案 3 :(得分:1)
Javascript没有词法范围(for-loop不会创建新的范围),您的解决方案是标准的解决方法。写这个的另一种方法可能是:
[1, 2].forEach(function(no){
//verify no
alert('setting '+no);
//timeout to recheck
setTimeout(function(){
alert('test '+no);
}, 500);
})
forEach()是在ECMAScript 5中引入的,并且存在于现代浏览器中但不存在于IE中。您可以使用my library来模拟它。