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;
以上代码的哪一部分是关闭?我被示例和解释所抛弃,因为我无法确定哪些部分被关闭,因此,我无法弄清楚为什么代码会导致未定义。
答案 0 :(得分:2)
在此示例中,list
,item
和i
正在关闭。您遇到了问题,因为在函数实际调用之前,这三个函数不会被推送到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
的情况下执行此操作的方法是创建一个函数,该函数将手动评估和关闭循环的每次迭代i
和list
,如此:
仅供参考:从最佳实践的角度来看,这实际上是正确的方法,因此您不会在循环内定义函数。
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]
。