JavaScript 变量绑定和循环

IT技术 javascript loops closures scope
2021-01-27 20:32:49

考虑这样的循环:

for(var it = 0; it < 2; it++)
{
    setTimeout(function() {
        alert(it);
    }, 1);
}

输出是:

=> 2
=> 2

我希望它是:0, 1。我看到了两种修复方法:

解决方案#1。

这个基于我们可以将数据传递给 setTimeout 的事实。

for(var it = 0; it < 2; it++)
{
    setTimeout(function(data) {
        alert(data);
    }, 1, it);
}

解决方案#2。

function foo(data)
{
    setTimeout(function() {
        alert(data);
    }, 1);
}

for(var it = 0; it < 2; it++)
{
    foo(it);
}

还有其他选择吗?

4个回答

除了您提出的两种方式之外,实际上并没有什么,但这是另一种方式

for(var it = 0; it < 2; it++)
{
    (function() {
        var m = it;   
        setTimeout(function() {
            alert(m);
        }, 1);
    })(); 
}

本质上,您需要在闭包中捕获变量值。此方法使用立即调用的匿名函数来捕获it局部变量中的外部变量值m

这是一个可以玩工作演示/edit添加到 URL 以查看代码

@digorydoo 因为it每次迭代的值都会改变,如果每次迭代中的值没有在每次迭代时立即执行的函数的上下文中捕获,那么将使用it传递给的函数setTimeout执行时存在的值分配ittom的值捕获闭包中每次迭代的值,从而在传递给的函数setTimeout执行时提醒每次迭代的预期值这有帮助吗?
2021-03-12 20:32:49
@digorydoo 在循环中声明的函数用括号括起来,后跟一组用于立即调用函数的括号。由于变量的作用域是声明它们的函数(或全局作用域,如果未在函数内声明),则it每次迭代中的值被分配给m作用域为立即执行的函数变量。
2021-03-15 20:32:49
我理解语法,但我很困惑,如果将变量“it”复制到其他变量“m”而不是仅使用“it”,为什么会有所不同。
2021-03-22 20:32:49
+1,但是有人可以解释我为什么这样做吗?!
2021-03-28 20:32:49
+1。但是,您可以通过将方法签名更改为:function(m) { /*code */ })(it);
2021-04-06 20:32:49

使用 let 关键字,您可以完全解决这个问题:

for(let it = 0; it < 2; it++)
{
    setTimeout(function() {
        alert(it);
    }, 1);
}
@PardeepJain 在 ES6 版本的 JS 中引入了 let 关键字。
2021-03-17 20:32:49
但是没有let在 javascript 中命名的关键字,我认为它是在typescript中
2021-04-03 20:32:49

与上述解决方案类似,但在 setTimeout 函数内部自调用

for(var it = 0; it < 2; it++)
{
    setTimeout(function(cur) {
        return function(){
           alert(cur);
        };
     }(it), 1);
 }

与其他解决方案类似,但在我看来更清洁:

for (var it = 0; it < 2; it++) {
  // Capture the value of "it" for closure use
  (function(it) {
     setTimeout(function() {
       alert(it);
     }, 1);
  // End variable captured code
  })(it)
}

这为捕获保留了相同的变量名称,并为整个循环执行此操作,将其与超时设置的逻辑分开。如果你想在块中添加更多的逻辑,你可以很容易地做到这一点。

我唯一不喜欢解决方案的是最后重复“它”。