如何在闭包中引用局部变量?

IT技术 javascript loops closures
2021-02-20 08:36:04

我正在阅读一篇文章(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”?当我变成 3 时 for 循环不会结束吗?如果它结束,项目不应该仍然是'item2'吗?还是 testList 调用函数时又创建了变量项?

5个回答

你很近...

为什么 item 的值为“item3”?当我变成 3 时 for 循环不会结束吗?

是的。

如果它结束,项目不应该仍然是'item2'吗?

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

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

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

哇这太简单了。我怎么错过了?整个从零开始计数的事情总是让我感到困惑。谢谢!
2021-04-28 08:36:04
如果这是您理解 javascript 闭包的唯一障碍,请拍拍自己的后背!我认为这个例子不必要地复杂。
2021-05-15 08:36:04

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;
}
@JP,感谢您的热烈响应。我现在明白为什么它是未定义的,但我可以问为什么你的例子有效。为什么现在为现在存储在 fnlist 中的每个函数创建了一个新的闭包?
2021-04-22 08:36:04
@Nick,它创建自己的闭包只是因为它是一个函数。函数形式封闭...所以,在每次循环,我们正在创建一个新的闭包,将保留当前值内的功能iitem
2021-04-23 08:36:04
@JP,谢谢伙计。我现在明白了。谢谢你的帮助。
2021-05-05 08:36:04
@Pointy,为什么它会创建自己的闭包?因为它是一个匿名函数?
2021-05-12 08:36:04
在对“result.push”的调用中声明和调用的那个函数创建了它自己的闭包。
2021-05-13 08:36:04

我认为您缺少的一点list[i]是因为它i是 3,list所以定义不足,并且仅定义为 0..2。

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

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

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

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

文本 'item' + list[2] 中的值。第三个列表项是3,所以文本是item3