JavaScript 对象赋值 : unexpected behaviour

标签 javascript oop object

假设我们有两个对象:

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 会更改 ab 的一个 bar 属性(间接)指向。

如果你想创建一个对象的副本,你可以使用a = Object.assign({}, b)或者(在ES2018+中)制作浅拷贝) a = {...b}。但如果您想复制 foo 引用的对象,则需要一个深层复制;请参阅 this question's answers

<小时/> 有些人,特别是在学术背景下,会说对象本身就是值(value),而“对象引用”是一个低级的管道概念,是我们注意到对象之间差异的唯一原因,例如,数字表明对象是可变的。我理解他们的观点,但我采取务实的态度。

关于JavaScript 对象赋值 : unexpected behaviour,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52663185/

相关文章:

vb.net - 与使用带有 getter 和 setter 的 Private 字段相比,VB.NET 中的 Property 关键字有哪些优势?

c++ - 我可以强制 C++ 类是非抽象的吗?

python - 如何在不实例化 Python 中的对象的情况下打印类属性?

c# - 动态、对象、变量

javascript - 切片 JavaScript 对象

javascript - 按字母过滤表格

javascript - 如何快速获取html文本输入值和输出

actionscript-3 - AS3 -TypeError #1009 - 有什么简单的方法可以找出*哪个*对象引用为空?

r - 对象创建时间戳

javascript - 如何从用户内存上传kml文件并在 map 上显示