假设我们有两个对象:
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
属性以及如何避免这种意外行为。
如何避免这种行为?我应该以不同的方式将对象分配给变量吗?
最佳答案
...I expect that a takes the value of b, not the reference...
这是不正确的。变量包含值。对于对象,该值是一个“对象引用”,它告诉 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
现在如何具有与 b
相同的“ref”值。很自然地,a.foo.bar = 3
会更改 a
和 b
的一个 bar
属性(间接)指向。
如果你想创建一个对象的副本,你可以使用a = Object.assign({}, b)
或者(在ES2018+中)制作浅拷贝) a = {...b}
。但如果您想复制 foo
引用的对象,则需要一个深层复制;请参阅 this question's answers 。
关于JavaScript 对象赋值 : unexpected behaviour,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52663185/