【问题标题】:JavaScript variable binding and loopJavaScript 变量绑定和循环
【发布时间】:2010-12-13 04:17:34
【问题描述】:

考虑这样的循环:

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

还有其他选择吗?

【问题讨论】:

    标签: javascript loops closures scope


    【解决方案1】:

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

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

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

    这里有一个 Working Demo 可以玩。将 /edit 添加到 URL 以查看代码

    【讨论】:

    • +1。但是,您可以通过将方法签名更改为:function(m) { /*code */ })(it); 来稍微修改它
    • +1,但是谁能解释一下为什么这行得通?!
    • @digorydoo 循环中声明的函数被括在括号中,后跟一组括号,用于立即调用该函数。由于变量的作用域为声明它们的函数(如果未在函数中声明,则为全局作用域),因此每次迭代中 it 的值被分配给作用域为所执行函数的 m 变量立即。
    • 我理解语法,但我很困惑为什么将变量“it”复制到其他变量“m”而不是只使用“it”会有所不同。
    • @digorydoo 因为it的值在每次迭代中都会改变,如果每次迭代中的值没有在每次迭代时立即执行的函数的上下文中被捕获,那么it的值为它在传递给setTimeout 的函数执行时存在,将被使用。将it 的值分配给m 会在闭包中捕获每次迭代中的值,从而在传递给setTimeout 的函数执行时提醒每次迭代的预期值。这有帮助吗?
    【解决方案2】:

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

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

    【讨论】:

    • 但在javascript中没有名为let的关键字,我认为它在打字稿中
    • @PardeepJain let 关键字已被引入 ES6 版本的 JS。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

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

      我不喜欢这个解决方案的唯一一点是在结尾处重复“它”。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-09-18
        • 2012-04-30
        • 2014-07-25
        • 1970-01-01
        • 2021-10-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多