【问题标题】:Javascript function scope [Javascript Essentials] [duplicate]Javascript函数范围[Javascript Essentials] [重复]
【发布时间】:2015-12-20 22:44:19
【问题描述】:

我在看 Travis Tidwell 的 Javascript Essentials,他解释了这段代码:

(function() {
    var messages = ['hello', 'there'];

    for (var i in messages) {
        setTimeout(function() {
            console.log(messages[i]);
        }, 10); 
    };
})();

它在控制台中重复“那里”两次,但我仍然不明白为什么。有人可以和我一起一步一步地浏览这段 javascript 吗?

【问题讨论】:

  • 这是臭名昭著的closure inside loop 问题。如果你搜索这个,你会发现大量的信息。本质上,这种行为的原因是 for 循环在超时触发时已经完成。在那个时间点,i 具有最后一个循环的值,对于两个超时。

标签: javascript arrays function for-loop anonymous-function


【解决方案1】:

JavaScript 具有函数作用域,而不是像其他语言那样的块作用域。因此,实际上只存在一个 i 变量。在调用setTimeout 中的代码时,i 已经设置为数组的最后一个索引。

很快,在 ECMAScript 6 中,我们可以使用 let 声明块范围的变量。见这里:Mozilla Reference: let

在此之前,解决此行为的一种方法是为需要独立于其他变量的变量创建一个新函数:

(function() {
  var messages = ['hello', 'there'];

  for (var i in messages) {
    (function(currentIndex) {
      setTimeout(function() {
        logToOutput(messages[currentIndex]);
      }, 10);
    })(i);
  };
})();

function logToOutput(msg) {
  document.getElementById("output").innerHTML += msg + "<br>";
}
&lt;div id="output"&gt;&lt;/div&gt;

【讨论】:

    【解决方案2】:

    每次代码绕过循环时,它都会设置一个事件处理程序,以便在 10 毫秒后记录messages[i] 的值。

    在任何这些超时过去 10 毫秒之前,i 的值已更改(通过for 循环)为1(因为这是数组中的最后一个属性名称)。

    然后第一次超时输出messages[1],然后第二次超时输出messages[1]


    1. 数组被创建并存储在messages
    2. i 设置为 0 并设置了超时
    3. i 设置为 1 并设置了超时
    4. 第一个超时函数运行,i 仍然是1
    5. 第二次超时函数运行,i 仍然是1

    【讨论】:

    • 为什么将“10”替换为“0”时行为相同?
    • @johnnyRose 浏览器会强制设置一个通常超过 10 毫秒的最小超时值。
    • 因为 (a) 强制执行最小超时并且 (b) JavaScript 是单线程的,所以事件循环在运行另一个函数时不会寻找要处理的事件。
    • @johnnyRose 作为概念证明来展示其他评论者所说的话,请参阅this fiddle。打开控制台,看到在任一“there”消息之前有两条“loop iteration”消息...
    • “但很奇怪,当设置超时时它不存储 i[0] 计数”——不,不是。这是 JavaScript 的一个基本特性。创建函数不会立即创建每个变量的本地范围副本并冻结它们的值。该函数在使用它之前甚至不会查看i
    猜你喜欢
    • 2018-07-31
    • 2011-11-15
    • 1970-01-01
    • 1970-01-01
    • 2011-03-01
    • 2015-03-11
    • 1970-01-01
    • 2010-12-07
    • 2010-09-16
    相关资源
    最近更新 更多