属性覆盖与合并的选择取决于是否需保留原对象其他字段:覆盖仅替换指定键、其余不变,适合局部更新;合并将多对象键值叠加、同名后生效,适合配置拼接;二者均为浅层操作,嵌套修改需手动或工具库。

属性覆盖和属性合并不是“选哪个更好”,而是取决于你是否需要保留原对象的其他字段。覆盖是用新值替换指定键,其余不变;合并是把多个对象的键值“叠”在一起,同名键后出现的生效。
覆盖适合:只改一两个字段,其余保持原样
典型场景是更新用户资料中的姓名或状态,不碰邮箱、头像、创建时间等其他字段。用展开运算符最直观:
-
单字段覆盖:
const updated = { user, name: "李四" };—— 只改 name,user 的 age、city 等全保留 -
多字段覆盖 + 新增:
const withStatus = { user, status: "active", updatedAt: Date.now() };—— 原有字段不动,只插入或替换列出的键 - 注意:覆盖不递归,只作用于顶层属性。如果
user.profile是个对象,{ user, profile: { nick: "阿七" } }会整个替换profile,而不是只改 nick
合并适合:把不同来源的配置、参数或片段拼成一个完整对象
比如把默认配置、环境配置、用户自定义配置按优先级叠在一起:
-
const config = { ...defaults, ...envConfig, ...userConfig };—— 后面的对象字段会覆盖前面同名的 - 常用于函数参数默认值处理:
function connect({ host = "localhost", port = 3000, ...opts }) { const final = { host, port, ...opts }; } - 合并也仅限浅层。若 defaults 和 opts 都含
headers对象,它们不会自动合并内部字段,而是后者完全取代前者
需要深层合并?那就得换方法
展开运算符和 Object.assign 都只做浅拷贝。如果结构嵌套(如 user.address.city),而你只想改 city 不动 address 其他字段,就不能靠覆盖或合并顶层对象来实现:
- 手动写:
{ ...user, address: { ...user.address, city: "深圳" } } - 用工具库:
_.merge(user, { address: { city: "深圳" } })(Lodash)或structuredClone配合递归赋值 - 注意性能:深合并要遍历所有嵌套层级,大数据量时别在热路径频繁调用
还要考虑不可变性与副作用
覆盖和合并都生成新对象,符合不可变数据原则。但如果你误用了赋值(obj.name = "新名字")或 push、splice 等原地修改方法,就不是覆盖/合并,而是直接污染原数据:
- 确认操作是否产生新引用:
updated === user应为 false - 避免对 props 或 store state 直接修改,尤其在 React/Vue 中,这会导致视图不更新或状态错乱
- 如果原对象有 getter/setter 或 Proxy 代理,展开可能丢失行为,需单独处理











