【问题标题】:JavaScript scoping with closure: help me understand带有闭包的 JavaScript 作用域:帮助我理解
【发布时间】:2011-04-09 23:21:11
【问题描述】:

运行以下代码:

for (var i=0; i<3; i++) { 
   setTimeout( function() { console.log(i); } , 500 ); 
}

输出“3”三次。它在创建内部函数时输出i 的最终值,而不是i 的值。

如果我希望输出为 1、2 和 3,我将如何编写这段代码?如何让它在定义函数时使用 i 的值而不是其最终值?

【问题讨论】:

    标签: javascript scope closures


    【解决方案1】:
    for (var i=0; i<3; i++) {
       setTimeout( function(val) { return function() { console.log(val); } }(i), 500 );
    }
    

    所以,在setTimeout 时间(在我们为setTimeout 定义函数的时候),我们正在调用以val 作为参数的匿名函数。这会为每个函数调用创建一个闭包,将val 的值存储在我们刚刚调用的函数的范围内。我使用了self-invoking function,它创建了一个即时的closure

    在您提供的代码中,代码创建了一个闭包,但对于整个代码的更大范围,因此i 是整个代码的本地,这意味着在运行时,匿名函数将使用其余代码使用的变量i

    【讨论】:

    • 这个例子使用了两个匿名函数,而@z5h 的回答使用了一个命名函数,这样可以更清楚地说明这个概念。
    【解决方案2】:

    显式闭包的现代替代方案(当你有一个双重包装的函数时可能会有点难以阅读)是Function#bind。对于尚未执行 ECMAScript 第五版的浏览器,一旦您 hacked in support,您可以说:

    for (var i=0; i<3; i++) {
        setTimeout(function(i) { console.log(i); }.bind(window, i), 500);
    }
    

    window 是函数内部的this 的值(这里不需要this,所以我们只使用默认的全局对象)。如果你只是调用另一个函数/方法,比如这里的console.log,你可以用它来完全删除函数表达式:

    for (var i=0; i<3; i++) {
        setTimeout(console.log.bind(console, i), 500);
    }
    

    【讨论】:

      【解决方案3】:

      替代方案:

      for (var i=0; i<3; i++) {
         (function(val){
             setTimeout(function() {
                 console.log(val);
             },500)
         }(i));
      }
      

      【讨论】:

        【解决方案4】:
        function f(i){
          return function(){console.log(i);};
        }
        
        for (var i=0; i<3; i++) { 
           setTimeout( 
             f(i)
           , 500 ); 
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-29
          • 2018-05-30
          • 2012-05-17
          • 1970-01-01
          相关资源
          最近更新 更多