JavaScript 变量范围

IT技术 javascript loops closures scope
2021-03-07 09:01:20

我遇到了一些 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个回答

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);

有关更多详细信息,请查看我对以下问题的回答:

@bdukes:回滚编辑,最后一个值iis 4,注意i < 5for 循环上的条件。
2021-04-25 09:01:20
@Bisbo:不客气,很高兴为您提供帮助!
2021-05-08 09:01:20
我将是 5 - 这是终止循环的值(也是 OP 报告的值)
2021-05-20 09:01:20
感谢您的回复和您对另一个问题的回答!它完美地解释了这一切!
2021-05-21 09:01:20

看看这个问题它可能会帮助您更好地理解范围和闭包,与您的问题非常相似。

感谢伟大的链接!它完美地解释了它!
2021-04-28 09:01:20

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

你可以用这样的东西来修复它:

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

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

从另一个函数返回函数的地方。

变量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);

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