这个例子的哪一部分是一个闭包?

时间:2017-09-22 17:11:23

标签: javascript closures

This link有关于JavaScript中闭包的示例。

在示例5中,我们有以下代码:

function buildList(list) {
  var result = [];
  for (var i = 0; i < list.length; i++) {
    var item = 'item' + i;
    result.push( function() {console.log(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(); //logs "item 2 is undefined" 3 times

在问题的描述中,陈述&#34;请注意,当您运行示例时,&#34; item2 undefined&#34;被提醒三次!这是因为就像前面的例子一样,buildList的局部变量只有一个闭包。&#34;

以上代码的哪一部分是关闭?我被示例和解释所抛弃,因为我无法确定哪些部分被关闭,因此,我无法弄清楚为什么代码会导致未定义。

3 个答案:

答案 0 :(得分:2)

在此示例中,listitemi正在关闭。您遇到了问题,因为在函数实际调用之前,这三个函数不会被推送到result的函数内部。

这实际上是ES6添加let关键字的原因的一个很好的示例,因为使用var只有1个i和1个item变量关闭的buildList函数,因此result数组中的所有函数都指向相同的i和相同的item变量,这些变量当然会在每次迭代中发生变化。它是吐出未定义的,因为它试图使用已设置的i的最后一个值,这将是list.length,这显然比列表中最后一个元素的索引多一个,所以当它试图访问list[i]时,它会以未定义的形式返回。

在for循环中将var i = ...更改为let i = ...,将var item = ...更改为let item = ...修复此问题并返回您期望的内容。

function buildList(list) {
  var result = [];
  for (let i = 0; i < list.length; i++) {
    let item = 'item' + i;
    // close on i and list. i does not get evaluated until fn gets called,
    // so we need to create a new i for each iteration of the loop, hence the use of let
    result.push( function() {console.log(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();

在不使用ES6 let的情况下执行此操作的方法是创建一个函数,该函数将手动评估和关闭循环的每次迭代ilist,如此:

仅供参考:从最佳实践的角度来看,这实际上是正确的方法,因此您不会在循环内定义函数。

function logFunction (index, list) {
  // inside here, index and list get evaluated as their current values in the loop
  // and then closed on for that specific value in the function being returned
  return function () {
    console.log("item" + index + " " + list[index]);
  }
}
function buildList(list) {
  var result = [];
  for (var i = 0; i < list.length; i++) {
    // pass i and list to function to manually create closure
    result.push( logFunction(i, list) );
  } 
  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(); //logs "item 2 is undefined" 3 times

答案 1 :(得分:1)

关闭是:

function() {console.log(item + ' ' + list[i])}

构建数组时,传递的i是对闭包函数范围之外的i变量的引用。 i变量总是相同并且正在递增(因为for循环)所以,当你调用每个闭包函数时,它会尝试访问list[list.length](因为i变为2循环,从0到list.length-1)因此你得到undefined异常。

答案 2 :(得分:1)

这将是闭包:function() {console.log(item + ' ' + list[i])},因为它使用i和父作用域中的其他变量。

运行代码会发生什么?

步骤:1。推送第一个有权访问父范围的函数 - i === 0

步骤:2。推送第二个功能 - 同样的事情 - &gt; i === 1(这与步骤1的i相同。 - 所以现在两个函数都有i === 1

...

步骤:(list.length-1)。推送数组中的list.length - 1函数 - &gt;现在i === list.length-1(但这与所有关闭都可以访问的i相同。

最后一步是来自i++的{​​{1}}。

现在你开始调用你创建的函数,但它们都引用了相同的for,它现在等于i,当它访问list.length时它超出了数组的范围所以它是list[list.length]