大多数答案都是正确的,因为将对象作为函数参数传递会破坏闭包,从而允许我们从循环内将事物分配给函数。但我想指出why情况就是如此,而且这不仅仅是闭包的特例。
你看,javascript 将参数传递给函数的方式与其他语言有点不同。首先,它似乎有两种实现方式,具体取决于它是原始值还是对象。对于原始值来说seems按值传递并为对象传递seems通过引用传递。
javascript 如何传递函数参数
实际上,JavaScript 的真正解释解释了这两种情况,以及它为何破坏闭包,仅使用一种机制。
javascript所做的实际上就是传递参数通过参考副本。也就是说,它创建了对该参数的另一个引用,并将该新引用传递给函数。
按值传递?
假设javascript中的所有变量都是引用。在其他语言中,当我们说变量是引用时,我们期望它的行为如下:
var i = 1;
function increment (n) { n = n+1 };
increment(i); // we would expect i to be 2 if i is a reference
但在 javascript 中,情况并非如此:
console.log(i); // i is still 1
这是一个经典的值传递,不是吗?
通过引用传递?
但是等等,对于对象来说,情况就不同了:
var o = {a:1,b:2}
function foo (x) {
x.c = 3;
}
foo(o);
如果参数按值传递,我们期望o
对象保持不变,但是:
console.log(o); // outputs {a:1,b:2,c:3}
这是经典的引用传递。因此,根据传递原始类型或对象的天气情况,我们有两种行为。
等等,什么?
但等一下,看看这个:
var o = {a:1,b:2,c:3}
function bar (x) {
x = {a:2,b:4,c:6}
}
bar(o);
现在看看会发生什么:
console.log(o); // outputs {a:1,b:2,c:3}
什么!这不是通过引用传递!数值不变!
这就是为什么我称它为传递参考副本。如果我们这样想的话,一切就都说得通了。我们不需要认为基元在传递给函数时具有特殊行为,因为对象的行为方式相同。如果我们尝试修改变量指向的对象,那么它的工作方式就像按引用传递,但如果我们尝试修改引用本身,那么它的工作方式就像按值传递。
这也解释了为什么通过将变量作为函数参数传递来破坏闭包。因为函数调用会创建另一个像原始变量一样不受闭包约束的引用。
尾声:我撒谎了
在我们结束之前还有一件事。我之前说过,这统一了原始类型和对象的行为。其实不是,原始类型还是有区别的:
var i = 1;
function bat (n) { n.hello = 'world' };
bat(i);
console.log(i.hello); // undefined, i is unchanged
我放弃。这没有任何意义。事情就是这样。