JavaScript变量范围

时间:2009-10-16 19:31:51

标签: javascript loops closures scope

我遇到了一些JavaScript代码问题。

脚本

setTimeout(function() {
    for (var i = 0; i < 5; i++) {
        setTimeout(function() {
            console.log(i);
        }, i * 200);
    }
}, 200);

输出

5,5,5,5,5而不是1,2,3,4,5

我可以理解为什么这不起作用,但我想知道是否有人可以向我解释发生了什么以及为什么它不起作用!

此外,如何克服此范围问题?

5 个答案:

答案 0 :(得分:5)

setTimeout回调函数是异步执行的,您所做的所有console.log调用都引用相同的i变量,并且在执行它们时,for循环已结束i包含4。

您可以将内部setTimeout调用包含在接受参数的函数中,以便存储对正在迭代的所有i值的引用,如下所示:

setTimeout(function() {
    for (var i = 0; i < 5; i++) {
      (function (j) { // added a closure to store a reference to 'i' values
        setTimeout(function() {
            console.log(j);
        }, j * 200);
      })(i); // automatically call the function and pass the value
    }
}, 200);

查看我对以下问题的回答以获取更多详细信息:

答案 1 :(得分:2)

看看this question。它可能有助于您更好地理解范围和闭包,非常类似于您的问题。

答案 2 :(得分:2)

您正在尝试创建一个包含变量“i”的闭包。但闭包仅在函数结束时创建。因此,如果您的函数是在 for 循环中创建的,那么它们都将具有上次迭代的值。

您可以使用以下内容修复它:

var createFunction = function(index) {
  return function() {
    console.log(index);
  }
};

for (var i = 0; i < 5; i++) {
  setTimeout(createFunction(i), i * 200);
}

从另一个函数返回函数。

答案 3 :(得分:1)

变量i存在于外部函数的范围内。

随着循环的运行而改变。

内部函数引用它。

尝试这样的事情:

var i_print_factory = function (value) {
  return function () {
    console.log(value);
  };
};

var init_timers = function () {
  for (var i = 0; i < 5; i++) {
    setTimeout(i_print_factory(i), i * 200);
  }
};

setTimeout(init_timers, 200);

答案 4 :(得分:0)

因为您在set timeout中使用的所有函数中访问相同的变量i。 setTimeout函数将函数设置为在与i变量相同的线程上触发将来的毫秒数。 i值不会复制到函数中,该函数在触发时引用实际变量i。因为你已经通过父函数循环直到i = 5并且这在其他任何有机会被触发之前完成,所以它们都显示为5。