浅拷贝不能保证嵌套对象的不可变性,仅复制第一层,深层仍共享引用;实现逻辑不可变需手动逐层重建修改路径上的对象,复用未修改分支。

浅拷贝本身无法在嵌套对象修改时保持不可变性——它只复制第一层,深层仍共享引用。所谓“通过浅拷贝保持不可变性”,实际是配合手动重建嵌套路径来实现逻辑上的不可变更新,而不是靠浅拷贝自动隔离嵌套结构。
为什么浅拷贝不能直接保护嵌套对象
像 Object.assign({}, obj) 或 {...obj} 这类操作,对 obj.user.profile.name 这样的嵌套属性完全无效:它们只新建了最外层对象,user 和 profile 仍是原引用。一旦你写 copy.user.profile.name = 'new',原始对象同步改变。
用浅拷贝 + 结构展开实现局部不可变更新
关键不是“拷贝整个对象”,而是只重建被修改路径上的每一层新对象,其余部分复用原引用(前提是不改)。这是函数式更新的常见模式:
- 修改顶层字段:直接用扩展运算符
{...obj, name: 'Alice'} - 修改二级字段(如
obj.user.age):{...obj, user: {...obj.user, age: 30}} - 修改三级字段(如
obj.user.address.city):{...obj, user: {...obj.user, address: {...obj.user.address, city: 'Shanghai'}}
每层都用浅拷贝新建对象,但只新建真正变化的那一支,其他分支保持原引用,既保证不可变性,又避免全量深拷贝开销。
立即学习“Java免费学习笔记(深入)”;
React / Redux 场景下的实用写法
这类写法在状态更新中很自然:
- 表单更新某个嵌套字段:
setForm({...form, user: {...form.user, email: e.target.value}}) - 数组中某项的嵌套属性更新:
items.map((item, i) => i === index ? {...item, meta: {...item.meta, updatedAt: Date.now()}} : item) - 避免错误写法:
const copy = {...obj}; copy.user.name = 'x';—— 这破坏了不可变性,且 React 可能无法检测到变化
什么时候该换深拷贝
如果嵌套层级深、动态路径多(比如根据字符串路径 'a.b.c' 更新),或需要批量修改多个分散字段,手写展开会变得冗长易错。这时应切换方案:
- 简单纯数据对象:用
JSON.parse(JSON.stringify(obj))(注意丢 Date/Function/undefined) - 生产环境推荐:
structuredClone(obj)(支持 Map/Set/Date/循环引用,但不支持 function 和 DOM) - 全能力兜底:
lodash.cloneDeep(obj)(兼容性好,可处理几乎所有类型)
记住:浅拷贝不是用来“防御嵌套修改”的工具,而是构建不可变更新链的砖块——用得对,轻量高效;用错了,反而掩盖 bug。


















