所以我想弄清楚这个难题:
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
result.push( function() {return i} );
}
return result;
}
console.log(fun1()[0]()) // returns 5?
该数组的第一个元素是否应该返回一个返回'0'的函数?
答案 0 :(得分:6)
不,它应该返回5.
该函数仍然具有对i的引用,在for之后为5。
答案 1 :(得分:6)
这是因为你的函数(闭包)维持了一个
i
而不是
i
快照。这意味着您的函数在执行时将知道当前i
的当前值。因为循环已经在那时完成,所以这将是循环设置的i
的最后一个值。那么,我们如何获取快照值而不是引用?幸运的是,数字参数由值传递...因此,您可以通过将i
传递给第二个函数来避免您的问题...(fiddle):
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
(function(x){
result.push(function() {
return x; // x now has i's snapshotted value
});
})(i); // passes i by value since it's numeric
}
return result;
}
alert(fun1()[0]())
[编辑] 让我们从等式中取出循环(fiddle):
var i = 1;
var fn = function() { // this closure maintains a reference to i
alert(i);
};
i = 10; // such that when we change i's value here
fn(); // it's reflected by the function call: alerting 10
所以,要“修复”这个(fiddle):
var i = 1;
var fn = (function(x) { // this is a self-executing anonymous function
return function() { // this closure maintains a reference to x
alert(x);
};
})(i); // we're passing i by value such that x will retain that value
i = 10; // when we change i here, it has no affect on x
fn(); // this will alert 1
答案 2 :(得分:3)
让我们分解一步一步发生的事情:
我们声明一个返回数组fun1()
的函数result
。
for
循环迭代5次,每次迭代递增i
。
请注意,在整个执行过程中,返回i
的匿名函数未被调用。
for
循环结束时i
的值为5
。
调用fun1()[0]
会返回一个数组result[]
,它有一个将引用存储到i
的函数,而不是值 of i
。
调用匿名函数,然后跟随该引用并返回i
的值5
。
答案 3 :(得分:2)
你应该阅读一些关于Javascript闭包的内容,仔细查看。
来自i
函数的变量fun1
可以从循环中的函数访问,但它是相同的变量,而不是克隆。
答案 4 :(得分:2)
内部函数中“i”的值将是返回时外部函数中“i”的值。因此,数组中的所有5个元素在调用时都将返回“5”。
答案 5 :(得分:1)
真是个问题!
问题是,在javascript中,就像在java中一样,整数值是按值传递的,而不是通过引用传递的。这是其他所有人告诉你的。但我假设你不知道这意味着什么。它是跨语言的标准,所以让我们看看我是否可以解释!
注意:这是一个残缺的解释。我真的不知道javascript是如何传递价值的,以及有趣的术语,如&#34; stack&#34;和&#34;堆&#34;和&#34;通过参考值&#34; (我认为在技术上是正确的吗?)可以提供更多的学术答案。希望我可以跳过这一切,并假设
因此,我们有pass-by-reference和pass-by-value。 在程序的内存中,当通过值时,变量&#34; i&#34;指向是一个字面数字。 当你通过引用传递时,变量&#34; i&#34;会指向某个其他对象的内存位置。
这是什么意思?
line 1. i = new Object();
line 2. i = new Object();
在第1行之后,该计划将在记忆中留下一点回家,并且它会给出一个位置 - 我不知道,有些公羊价值(比如说。我不喜欢&#34; i&#34; #39;真的知道引擎盖下的javascript)。然后,在该值中,它将放入另一个值。所以我们有:
i -> 0x67 -> 0x68
所以程序知道,当它看到值&#34; i&#34;时,它会进入内存位置0x67,并获得值0x68。它还知道该值指向另一个内存位置。在这种情况下我们有
...
0x67 -> 0x68 (in bytes)
0x68 -> some byte representation of a new Object()
0x69 -> i don't know! some more bytes. they're not important right now.
...
第2行开始后,我们
i ->0x67 -> 0x69 (in bytes)
和
0x69 -> some byte representation of a new Object()
给予:
...
0x67 -> 0x68 (in bytes)
0x68 -> some byte representation of a new Object()
0x69 -> some byte representation of a new Object()
...
现在,碰巧,如果你在两个新的Ojbects上如此平等,那么他们就好了(我认为)。 这里要注意的重要一点是,在第二行之后,i的值已经从第一个存储位置改变到第二个存储位置。
所以,如果你这样做了:
i = new Object()
j = i
i = new Object()
你会得到:
i -> 0x67 -> 0x68 ->byte for a new Object
j -> 0x69 -> 0x68 -> byte for a new Object
i -> 0x67 -> 0x70 -> byte for another new Object. Same bytes as above.
请参阅? &#34; J&#34;得到&#34; i&#34;中的实际字节值,这意味着&#34; j&#34;获取值0x68,这是新对象的内存位置。在第三行之后,j具有第一个对象的位置,并且&#34; i&#34;具有第二个对象的位置。
但是,你对j做的任何改变 - 比如说
j.x = "moo"
不会出现在&#34; i&#34;指着。它只会出现在&#34; j&#34;宾语。因为,请记住,j指向与我指向的对象完全不同的位置的完全不同的对象。
好的, 让我们放弃,让我们回归到价值传递。
下面
i = 6;
j = i;
i = 7;
所以我们得到了
i -> 0x67 -> 6 (well, the bytes for 6, anyway)
j -> 0x68 -> 6 (again, bytes for 6. the same bytes. We're pointing to i here)
i -> 0x67 -> 7
并且,如果我们要看j?
j -> 0x68 -> 6
所以,因为我们更新了&#34; i&#34;,我们也不会更新&#34; j&#34;,得到它?这些字节被保存,j在这里得到它自己的字节副本。
好的,所以如上所述,请查看您的代码:
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
result.push( function() {return i} );
}
return result;
}
是的,所以在for循环中,你创建的函数包含对i 的引用,for循环的动作是递增i。
你很可能会坚持下去!&#34;什么 - 你觉得这样的内心吗? - 因为我刚才注意到数据是如何通过整数值(或一般数字)传递的。这里的问题是:
从此页面: https://developer.mozilla.org/en/JavaScript/Guide/Closures
闭包是一种特殊的对象,它结合了两个东西:一个函数,以及创建该函数的环境。环境由创建闭包时在范围内的任何局部变量组成。
所以......我粘贴了上面的原因 - 闭包是一个函数(fun1,比如说)和创建函数的环境。和环境?它由范围内的局部变量(噢,我怎么样?)组成。
所以,直言不讳......正如我仍然在整个循环的范围内,同样的&#34;我&#34;是我们生成的每个闭包的范围。那我呢?我正在更新它的价值。您没有传递i的快照,您正在尽可能地确定参考对象本身。它不再是价值传递,我们有点可以通过引用来回传。
所以,在记忆中,我们有
i -> 0x67 -> 0
i -> 0x67 -> 1
i -> 0x67 -> 2
i -> 0x67 -> 3
i -> 0x67 -> 4
i -> 0x67 -> 5 (yeah, the loop increments i, then fails the test of i <5. but is still 5)
在你的数组中,你要放一个只能吐出i值的函数。但是你一直在增加我。您放入该阵列的每个功能都将吐出&#34; 5&#34;。
更进一步......
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
result.push( function() {return i++;} );
}
return result;
}
alert(fun1()[0]());
alert(fun1()[0]());
alert(fun1()[4]());
在这里,我们正在测试我们是否可以&#34;保持&#34;在初始环境之外的变化。如果您运行此操作,则无法查看存储的任何更改。在第一个例子中,我们增加&#34; i&#34;到6 - 但是当我们再次运行这个例子时,我又是5 - 没有保留增量。
最后,尝试使用另一个数组函数显示该值也没有增加。
所以,对你而言,你想要的是以某种方式及时冻结i 进入数组时的价值
这非常棘手。
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
result.push( eval("(function() { return " + i+";})"));
}
return result;
}
在哪里&#34; eval&#34;是一个花哨的javascript函数,它创建代码在运行时运行 - 动态,我的意思是。我用括号括起来,因为
Why does JavaScript's eval need parentheses to eval JSON data?
我不知道关于eval的超级数量!
接下来,其他一个解决方案显示了这一点:
(function(x){
result.push( function() {return x} );
})(i);
放在for循环的上下文中 - 给出
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
(function(x){
result.push( function() {return x} );
})(i);
}
return result;
}
这样做是为了在其中存在闭包创建一个 new 环境。在这种环境下,&#34; x&#34;永远固定,永远是我当前(即在实例化时)的值。因此,当我们的数组尝试访问给定的闭包时,它存在于x被修复的新环境中。如果我们这样做了:
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
(function(){
result.push( function() {return i;} );
})();
}
return result;
}
然后我们遇到了同样的问题。 &#34;我&#34; 仍然存在于循环的环境(或范围,如果你愿意)中,所以无论你多少次将其粘贴在函数中,它都会被拾取。
如果有人读到这篇文章并认为我写的是bollocks,请告诉我!我有点想知道js如何处理内存管理。