循环中的javascript var声明

时间:2010-04-28 15:58:39

标签: javascript variables scope

/*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);
}

有关如何以更直接的方式解决此问题的任何想法?或者这是唯一的方法吗?

4 个答案:

答案 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来模拟它。