JavaScript跨对象属性赋值:为何修改john.address.city会改变sherlock的城市值?
为什么输出结果是
Malibu而非London 核心原因是JavaScript中对象类型的赋值是引用传递,和原始值的赋值逻辑有本质区别。
逐行拆解实际运行逻辑
- 初始化
sherlock对象时,堆内存会分别创建两个独立的对象实例:- 第一层主对象:存储属性
surname: 'Holmes'和address指针 - 第二层address对象:存储属性
city: 'London'
栈上的sherlock变量、sherlock.address属性,存储的都是对应堆内存对象的引用地址,不是对象实体本身。
- 第一层主对象:存储属性
- 初始化
john对象时,执行address: sherlock.address的过程,并没有把sherlock的address对象完整复制一份给john,而是把sherlock.address里存的堆内存地址,原样赋值给了john.address。这一步完成后,sherlock.address和john.address两个属性,会指向堆内存里同一个公共对象,不存在任何隔离。 - 后续两行赋值的实际效果:
john.surname = 'Lennon':surname是字符串原始值,赋值时会直接把新值写在john自己的属性上,完全不会影响sherlock的surname属性——这也是造成认知偏差的原因:原始值的赋值确实是值拷贝,两个变量不会互相影响,但对象类型的赋值规则完全不同。john.address.city = 'Malibu':这行代码会顺着john.address存的引用地址,找到堆内存里的公共address对象,把它的city属性值改成Malibu。因为sherlock.address也指向这同一个对象,所以最后读取sherlock.address.city时,拿到的自然是修改后的Malibu,而不是初始的London。
认知误区说明
你原本以为“address: sherlock.address是把sherlock的address值给john,两个对象不会互相干扰”,这个逻辑仅对字符串、数字、布尔值这类原始值成立。只要赋值的是对象类型,传递的就是对象的内存引用,相当于两个人持有同一套房屋的钥匙,不管谁进屋改动了陈设,另一人开门看到的都是改动后的状态,不存在两套独立房屋的隔离效果。
如果需要让两个对象的address完全独立,可以在赋值时主动拷贝生成新对象,比如写address: { ...sherlock.address },这样会生成一个全新的address实例给john,后续修改john的city属性就不会对sherlock造成影响。
内容的提问来源于stack exchange,提问作者juexu
相关产品推荐
相关产品推荐

