假设我们有两个对象:
var a = { foo: { bar: 1 } }
var b = { foo: { bar: 2 } }
如果将对象b设置为a(a = b
),则我希望a取b的值,而不是引用的值。因此,在这种情况下:
a = b
a.foo.bar = 3
console.log(b.foo.bar);
我希望最后一个console.log
显示2,而不是3。为什么?只是因为我更改了与a
而不是b
相关的属性。
我不明白为什么JavaScript还会更改b
属性以及如何避免这种意外行为。
如何避免这种行为?是否应该以其他方式将对象分配给变量?
答案 0 :(得分:3)
...我希望a取b的值,而不是引用的值...
那是不对的。变量包含值。对于对象,该值是“对象引用”,它告诉JavaScript引擎该对象在内存中的其他位置。因此a = b
使a
“指向”同一对象b
“指向”。如果更改该对象的属性,则无论您从中获取引用的变量如何,都将观察到这些更改。
初始设置后,您的内存中会出现以下内容(省略了许多详细信息):
+−−−−−−−−−−−−−−+ a: Ref5465−−−−>| (object) | +−−−−−−−−−−−−−−+ +−−−−−−−−−−+ | foo: Ref8761 |−−−−>| (object) | +−−−−−−−−−−−−−−+ +−−−−−−−−−−+ | bar: 1 | +−−−−−−−−−−+ +−−−−−−−−−−−−−−+ b: Ref1574−−−−>| (object) | +−−−−−−−−−−−−−−+ +−−−−−−−−−−+ | foo: Ref4456 |−−−−>| (object) | +−−−−−−−−−−−−−−+ +−−−−−−−−−−+ | bar: 2 | +−−−−−−−−−−+
(这些“ ref”值当然是概念性的;我们从未真正看到它们。)
然后,当您执行a = b
时,a
曾经引用的对象才有资格进行垃圾回收,而您可以使用它:
a: Ref1574−−+ | | | +−−−−−−−−−−−−−−+ +−>| (object) | | +−−−−−−−−−−−−−−+ +−−−−−−−−−−+ | | foo: Ref4456 |−−−−>| (object) | | +−−−−−−−−−−−−−−+ +−−−−−−−−−−+ b: Ref1574−−+ | bar: 2 | +−−−−−−−−−−+
请注意,a
现在如何具有相同的“ ref”值b
。自然,a.foo.bar = 3
更改了bar
和a
(间接)指向的一个b
属性。
如果要对对象进行复制,则可以使用a = Object.assign({}, b)
或(在ES2018 +中)a = {...b}
进行浅表复制。但是,如果要复制对象foo
所指的对象,则需要 deep 复制。参见this question's answers。