【问题标题】:How does variables defined with `let` act inside a `for` loop [duplicate]用`let`定义的变量如何在`for`循环中起作用[重复]
【发布时间】:2016-11-02 06:39:43
【问题描述】:

documentation for the let statement in MDN,有这个示例代码:

var list = document.getElementById("list");

for (let i = 1; i <= 5; i++) {
  let item = document.createElement("li");
  item.appendChild(document.createTextNode("Item " + i));

  item.onclick = function (ev) {
    console.log("Item " + i + " is clicked.");
  };
  list.appendChild(item);
}

然后他们说:

上面的示例按预期工作,因为 (匿名)内部函数是指五个不同的实例 变量 i.

我不明白为什么会有“变量i 的五个不同实例。

for 循环中的第一条语句总是执行一次,不是吗? 所以let 语句应该只执行一次...
一旦代码到达迭代的结尾,它就会检查第二条语句中的条件。

怎么会,根据他们写的,每次迭代都有一个i 的新实例?

【问题讨论】:

  • 不是你要的,但我上周碰巧测试了这种行为,发现 IE 没有正确实现它。 (Chrome 做到了。)
  • 在 ECMA 6 规范中,当使用 let 表达式时,每次迭代都会创建一个新的词法范围,链接到前一个范围。 ecma-international.org/ecma-262/6.0/…
  • 要理解这一点,请将 let 更改为 var 并查看单击循环创建的元素时会发生什么
  • 第一条语句只执行一次,但我相信第三条语句(I++)等于let i = i + 1,它执行了四次……对吗?
  • @Jamiec,我了解发生了什么,但不完全是为什么

标签: javascript for-loop let


【解决方案1】:

当使用 let 表达式时,每次迭代都会创建一个新的词法范围,链接到前一个范围。

这意味着每个闭包都会捕获一个不同的实例。

这是根据ECMA 6 specification,但不确定它是否会在所有浏览器中以相同的方式工作。

也不确定性能影响。我宁愿谨慎使用此功能。

【讨论】:

  • 在使用let IIFE 之前,为了达到同样的效果——性能可能更差。那么你的建议是什么?
  • @LUH3417 我只是说我not sure 关于这种结构在不同浏览器中的性能。例如,查看为铬报告的这个问题bugs.chromium.org/p/v8/issues/detail?id=4762
  • @LUH3417 是的,可能 iffy-s 在性能方面更差。所以我现在最好不要推荐任何东西
【解决方案2】:

在 Javascript 中,变量传统上是函数范围的。块,例如 for...loop 语句,不会创建新范围。

如果您在函数中的任何位置(例如,在 for 循环中)使用 var 声明变量,则由于 hoisting,它将在范围顶部仅声明一次 ,并且在整个函数范围内只会有一个它的实例。

当您在 for...循环中调用回调时,可能会导致问题。

// with hoisting, i is only declared once
for (var i in items) {  
    // the fn is called items.length times, before any callback is invoked
    _fetchItems(items[i], function() { 
        console.log("fetched for ", items{i]); 
        // for all callbacks, i is the same value items.length-1 
        // because they are called after the loop is complete
    });
}

或者在你的例子中:

// with hoisting, i is only declared once
for (var i = 1; i <= 5; i++) {

    // with hoisting, item is only declared once
    var item = document.createElement("li");
    item.appendChild(document.createTextNode("Item " + i));

    // this function will be called after the for...loop is complete
    // so i value is unique: 5 + 1 = 6
    item.onclick = function (ev) {
        // => always return "Item 6 is clicked"
        console.log("Item " + i + " is clicked."); 
    };
    list.appendChild(item);
}

相反,let 变量仅限于最近的块(即大括号之间的任何代码段)。

在您的示例中,变量i 的新实例被声明,每次执行for...循环中的块。 i从 1 到 5,因此该块有 5 次执行,因此有 5 个变量实例。

它们将各自返回预期值“第 1 项已单击。”、“第 2 项已单击。”等

【讨论】:

  • 重点是for 循环中的第一条语句总是执行一次。如下完成for (var i = 1; i++; i &lt; 5) { let t = i; ... } 不会引起任何问题。
猜你喜欢
  • 2016-05-13
  • 2013-06-15
  • 2020-04-05
  • 2020-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多