【问题标题】:How are local variables referenced in closures? [duplicate]闭包中如何引用局部变量? [复制]
【发布时间】:2009-06-01 02:15:24
【问题描述】:

我正在阅读一篇文章(JavaScript Closures for Dummies),其中一个示例如下。

function buildList(list) {
  var result = [];
  for (var i = 0; i < list.length; i++) {
    var item = 'item' + list[i];
    result.push( function() {alert(item + ' ' + list[i])} );
  }
  return result;
}

function testList() {
  var fnlist = buildList([1,2,3]);
  // using j only to help prevent confusion - could use i
  for (var j = 0; j < fnlist.length; j++) {
    fnlist[j]();
  }
}

testList();

调用 testList 时,会出现一个显示“item3 undefined”的警告框。文章有这样的解释:

当匿名函数在fnlist[j](); 行上被调用时,它们都使用相同的单个闭包,并且它们使用该闭包中 i 和 item 的当前值(其中 i 的值为 3,因为循环已完成,并且 item 的值为 'item3')。

为什么 item 的值为 'item3'?当 i 变为 3 时,for 循环不会结束吗?如果结束,项目不应该仍然是“项目2”吗?还是testList调用函数时再次创建了变量项?

【问题讨论】:

  • 是否是导致文本超出预期链接的错误?

标签: javascript loops closures


【解决方案1】:

你已经接近了……

为什么 item 的值为 'item3'?当 i 变为 3 时,for 循环不会结束吗?

是的。

如果它结束了,项目不应该仍然存在 '项目2'?

不。这个例子有点棘手。在循环的最后一次迭代中,i 为 2,但它引用了 list 数组的第三个元素,即 3。换句话说,item == 'item' + list[2] == 'item3'

还是testList调用函数时再次创建了变量项?

不,你第一次几乎是对的。我想你只是错过了item[2] 的值为 3。

【讨论】:

  • 哇,这太简单了。我怎么错过了?整个从零开始计数的事情总是让我感到困惑。谢谢!
  • 如果这是您理解 javascript 闭包的唯一障碍,请给自己点赞!我认为这个例子过于复杂了。
【解决方案2】:

buildList 中的 for 循环在您执行以下操作之前完成:

for (var j = 0; j < fnlist.length; j++) {
  fnlist[j]();
}

...因此,到那时(当您调用每个函数时),变量 item 将是最后分配给它的任何内容(即“item3”),i 将是 3(如最后一次i++ 操作的结果),而list[3]undefined

这与循环在您调用 closure'd 函数之前完成这一事实有关。为了防止这种情况,您可以创建一个新的闭包,如下所示:

function buildList(list) {
  var result = [];
  for (var i = 0; i < list.length; i++) {
    var item = 'item' + list[i];
    result.push(
        (function(item, i){
            // Now we have our own "local" copies of `item` and `i`
            return function() {
                console.log(item + ' ' + list[i])
            };
        })(item, i)
    );
  }
  return result;
}

【讨论】:

  • @J-P,感谢您的好评。我现在明白为什么它是未定义的,但我可以问为什么你的例子有效。为什么现在为存储在 fnlist 中的每个函数都创建了一个新的闭包?
  • 在“result.push”调用中声明和调用的函数会创建自己的闭包。
  • @Pointy,为什么它会创建自己的闭包?因为它是一个匿名函数?
  • @Nick,它创建自己的闭包仅仅是因为它是一个函数。函数中的函数形成一个闭包......因此,在循环的每次迭代中,我们都将创建一个新的闭包,它将保留 iitem 的当前值。
  • @J-P,谢谢。我现在明白了。感谢您的帮助。
【解决方案3】:

我认为您缺少的一点是 list[i] 未定义,因为 i 是 3,而 list 仅定义为 0..2。

【讨论】:

    【解决方案4】:

    正如您所说,list 变量存储在闭包中。

    实际上您可以访问list 变量,但您正在尝试访问list[3]。毕竟i变量也是作为闭包存储的,在调用console.log函数时它的值为3。

    【讨论】:

      【解决方案5】:

      当 i 变为 3 时循环结束,但是存储在闭包中并由 alert 显示的“item”变量设置为

      var item = 'item' + list[i];
      

      文本“项目”+ 列表 [2] 中的值。第三个列表项是3,所以文本是item3

      【讨论】:

        猜你喜欢
        • 2013-07-17
        • 2011-08-15
        • 1970-01-01
        • 2020-06-30
        • 1970-01-01
        • 2010-10-13
        • 2016-09-02
        • 2017-12-22
        • 2011-01-21
        相关资源
        最近更新 更多