我在javascript闭包中偶然发现了一个非常奇怪的行为。如果闭包的“全局”变量是一个对象,那么从它到另一个变量的任何赋值就像某种指针一样。一个简单的代码应该更好地解释我的意思:
function closure() {
var foo = {key1 : 'value 1'};
return {
method: function(){
var bar = foo;
bar.key2 = 'value2';
return 'foo has attributes : ' + foo.key1 + ' and ' + foo.key2
}
}
}
var someVar = closure();
someVar.method(); // returns "foo has attributes : value 1 and value2" (!!!)
我显然希望获得foo has attributes : value 1 and undefined
,因为只有bar
被修改了...但是,该脚本实际上修改了bar
和foo
,让我感到困惑
我也注意到,当foo
和bar
是字符串或数字时,一切都按预期工作 - 修改bar
不会影响foo
。
function closure() {
var foo = 10;
return {
method: function(){
var bar = foo;
bar += 1;
return 'foo is : ' + foo + ' and bar is ' + bar
}
}
}
var someVar = closure();
someVar.method(); // returns "foo is : 10 and bar is 11"
我花了整整一个晚上试图弄清楚这种行为的原因......没有运气。我得到了相同的结果是SpiderMonkey和V8(都在Chrome和node.js上)。
我想要做的当然是从第一个例子修改bar而不影响foo。任何帮助将非常感激。
答案 0 :(得分:2)
执行var bar = foo;
时,bar
和foo
都指向同一个对象。如果您不想更改foo
,则需要克隆 foo
。克隆时,您将获得一个全新的对象,bar
将指向该对象。 This answer有一些关于在Javascript中克隆对象的信息。
此外,此行为不仅限于闭包。它将在常规Javascript代码中发生:
var foo = {a: 10, b: 6};
var bar = foo;
bar.c = 7;
console.log(foo);
将foo
显示为包含a
,b
,和 c
的属性。
您所看到的是大多数面向对象语言的标准行为。在处理对象时,您正在处理引用到这些对象而不是特定的实例值。您没有看到这个数字,因为在Javascript中它们是原始类型,因此您处理实际值而不是对这些值的引用。
答案 1 :(得分:2)
这是几乎所有面向对象语言的完全正常的预期行为,当然是我能想到的所有动态的语言。变量保存对象的引用,而不是对象本身。它就像一个指针,但是从内存位置的细节中抽象出来;你不能对它做任何算术。就使用它而言,它看起来像变量直接保存对象,但事实并非如此。如果将对象传递给函数,即使JavaScript是按值传递,该函数也可以修改对象。如果将它分配给另一个变量,则不会获得副本,而是别名:两个指向同一对象的变量。
如果要制作副本,则必须明确这样做。没有内置方法可以自动执行此操作,但请参阅this question了解某些选项。