JS将旧对象赋值给新对象后修改新对象同步更新旧对象的原因及优化方案
运行原理说明
JavaScript中的对象属于引用类型,变量存储的不是对象本身的内容,而是对象在堆内存中的存储地址:
你给出的示例代码运行后oldObject的hello属性被修改,原因就是newObject["newStuff"] = oldObject这一步没有生成新的对象副本,只是把oldObject的内存地址赋值给了newObject的newStuff属性,二者指向同一块内存空间,所以通过任意一个引用修改对象属性,另一个引用读取到的也会是修改后的值。
你提到的两种赋值操作的差异也和这个特性直接相关:
x.member = "bar":在x原本指向的内存地址上修改对象属性,所有指向该地址的引用都会感知到变更,属于修改原对象操作x = { member: "bar" }:在堆内存中开辟了一块新空间存储新的对象,再把新的地址赋值给x变量,x原本指向的旧对象没有被修改,其他指向旧对象的引用不会受影响,属于替换变量指向操作
大对象替换赋值的简洁写法
如果需要使用第二种赋值逻辑,又不想手动重写所有原有属性,可以用以下方案:
- 浅更新用对象扩展运算符
如果对象只有一层属性,直接用...扩展运算符保留原有属性,仅覆盖需要修改的部分即可:
// 原有x有10+个属性,仅修改需要调整的属性,其他自动保留 x = { ...x, member: "bar", // 其他要修改的属性直接追加即可,不需要重复写原有属性 otherKey: "newValue" }
- 含嵌套对象的深更新
如果对象存在多层嵌套,扩展运算符是浅拷贝,要避免修改原嵌套对象的话搭配structuredClone做深拷贝:
x = { ...structuredClone(x), member: "bar", nestedProp: { ...structuredClone(x.nestedProp), nestedKey: "newNestedValue" } }
- 高频更新封装成工具函数
如果同一个结构的对象需要频繁更新,可以把逻辑封装成复用函数,调用时更简洁:
function createUpdatedObject(oldObj, newFields) { return { ...oldObj, ...newFields } } // 调用示例 x = createUpdatedObject(x, { member: "bar", age: 26, address: "xxx" })
这种写法也是前端框架中「不可变数据更新」的标准实现方式,哪怕对象属性再多也不会导致代码混乱。
内容的提问来源于stack exchange,提问作者user16152074
相关产品推荐
相关产品推荐

