Ard*_*eol 40 javascript function
最近,我发现自己需要创建一系列函数.这些函数使用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]());
Run Code Online (Sandbox Code Playgroud)
输出为Num:5和Fun:10.
经过研究,我找到了一段有效的代码,但我很难理解为什么它有效.我用这个例子在这里复制了它:
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]());
Run Code Online (Sandbox Code Playgroud)
我知道它与范围界定有关,但乍一看似乎它与我的天真方法没有任何不同.我有点像Javascript的初学者,所以如果我可能会问,为什么使用这个函数返回函数技术会绕过作用域问题?另外,为什么(i)包括在最后?
非常感谢你提前.
Ted*_*opp 37
如果使用不掩盖循环变量名的参数名,第二种方法会更清晰一点:
funArr[funArr.length] = (function(val) { return function(){ return val; }})(i);
Run Code Online (Sandbox Code Playgroud)
您当前代码的问题是每个函数都是一个闭包,它们都引用相同的变量i.运行每个函数时,它返回函数运行时的值i(这将比循环的极限值多一个).
更清楚的方法是编写一个返回所需闭包的单独函数:
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; };
}
Run Code Online (Sandbox Code Playgroud)
请注意,这与我的答案中的第一行代码基本相同:调用一个返回函数并将值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; };
}
Run Code Online (Sandbox Code Playgroud)
通过这一点变化,每个funArr元素都是一个闭包,在每个循环迭代中做一个不同的 i对象.有关更多信息let,请参阅2015年的Mozilla Hacks帖子.(如果您的目标是不支持的环境,请let坚持我之前写的内容,或者在使用之前通过转发器运行最后一次.
让我们研究一下代码的作用,并分配虚构的函数名称:
(function outer(i) {
return function inner() {
return i;
}
})(i);
Run Code Online (Sandbox Code Playgroud)
在这里,outer收到一个参数i.JavaScript使用函数作用域,这意味着每个变量仅存在于其定义的函数中.i这里定义outer,因此存在于outer(以及其中包含的任何作用域)中.
inner包含对变量的引用i.(请注意,它并没有重新定义i的参数或用var关键字!)JavaScript的作用域规则指出,这种提法应该连接到第一封闭范围,在这里是的范围outer.因此,i内在inner指的是同一i内outer.
最后,在定义函数之后outer,我们立即调用它,向它传递值i(这是一个单独的变量,在最外层的作用域中定义).该值i包含在其中outer,现在不能通过最外层范围内的任何代码更改其值.因此,当最外层i在for循环中递增时,i内部outer保持相同的值.
记住我们实际上已经创建了许多匿名函数,每个函数都有自己的范围和参数值,希望清楚这些匿名函数是如何保留自己的值的i.
最后,为了完整性,我们来看看原始代码发生了什么:
for(var i = 0; i < 10; ++i){
numArr[numArr.length] = i;
funArr[funArr.length] = function(){ return i; };
}
Run Code Online (Sandbox Code Playgroud)
在这里,我们可以看到匿名函数包含对最外层的引用i.当该值发生变化时,它将反映在匿名函数中,该函数不会以任何形式保留其自己的值副本.因此,由于i == 10在我们去调用我们创建的所有这些函数的最外层范围内,每个函数都将返回该值10.
我建议学习一本像JavaScript:The Definitive Guide这样的书,以便更深入地理解JavaScript,这样你就可以避免像这样的常见陷阱.
这个答案还特别提供了关于闭包的合理解释:
当你调用
function() { return i; }
Run Code Online (Sandbox Code Playgroud)
该函数实际上在父调用对象(范围)上进行变量查找,这是我定义的位置.在这种情况下,i被定义为10,因此这些函数中的每一个都将返回10.这是有效的原因
(function(i){ return function(){ return i;}})(i);
Run Code Online (Sandbox Code Playgroud)
是通过立即调用匿名函数,创建一个新的调用对象,其中定义了当前的i.因此,当您调用嵌套函数时,该函数引用匿名函数的调用对象(它定义了调用时传递给它的任何值),而不是最初定义i的范围(仍然是10) .
| 归档时间: |
|
| 查看次数: |
28804 次 |
| 最近记录: |