最近,我发现自己需要创建一个函数数组。这些函数使用 XML 文档中的值,我使用 for 循环遍历适当的节点。然而,在这样做时,我发现数组中的所有函数只使用了 XML 表的最后一个节点(对应于 for 循环的最后一次运行)。
下面是一个展示这一点的例子:
var numArr = [];
var funArr = [];
for(var i = 0; i < 10; ++i){
numArr[numArr.length] = i;
funArr[funArr.length] = function(){ return i; };
}
window.alert("Num: " + numArr[5] + "\nFun: " + funArr[5]());
输出为 Num:5 和 Fun:10。
经过研究,我发现了一个 a segment of code that works ,但我很难准确理解它为何起作用。我在这里使用我的示例复制了它:
var funArr2 = [];
for(var i = 0; i < 10; ++i)
funArr2[funArr2.length] = (function(i){ return function(){ return i;}})(i);
window.alert("Fun 2: " + funArr2[5]());
我知道它与范围界定有关,但乍一看,它的表现似乎与我天真的方法没有任何不同。我是 Javascript 的初学者,所以如果我可能会问,为什么使用这种函数返回函数技术可以绕过作用域问题?另外,为什么 (i) 包含在最后?
非常感谢您。
最佳答案
如果使用不屏蔽循环变量名的参数名,第二种方法会更清晰一些:
funArr[funArr.length] = (function(val) { return function(){ return val; }})(i);
您当前代码的问题是每个函数都是一个 closure它们都引用相同的变量 i
。当每个函数运行时,它返回函数运行时 i
的值(这将比循环的限制值大 1)。
更清晰的方法是编写一个单独的函数来返回您想要的闭包:
var numArr = [];
var funArr = [];
for(var i = 0; i < 10; ++i){
numArr[numArr.length] = i;
funArr[funArr.length] = getFun(i);
}
function getFun(val) {
return function() { return val; };
}
请注意,这与我的回答中的第一行代码基本相同:调用一个返回函数的函数并将 i
的值作为参数传递。它的主要优点是清晰。
编辑:现在几乎所有地方都支持 EcmaScript 6(抱歉,IE 用户),您可以使用更简单的方法——使用 let
关键字而不是 var
循环变量:
var numArr = [];
var funArr = [];
for(let i = 0; i < 10; ++i){
numArr[numArr.length] = i;
funArr[funArr.length] = function(){ return i; };
}
有了这个小改动,每个 funArr
元素都是一个闭包边界,在每次循环迭代时执行一个不同 i
对象。有关 let
的更多信息,请参阅 this Mozilla Hacks post from 2015 . (如果您的目标环境不支持 let
,请坚持我之前写的内容,或者在使用前通过转译器最后运行它。
关于Javascript:在 For 循环中创建函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19696015/