展开运算符仅浅拷贝第一层属性,深层引用类型共享内存,无法自动穿透嵌套,且忽略不可枚举属性、原型链、symbol键及特殊值,三层以上嵌套或含函数/日期时应换用structuredclone等深拷贝方案。

展开运算符({...obj})做嵌套对象拷贝时,只复制第一层属性,深层结构仍共享引用——这不是“不够好”,而是设计如此。它本就不是为多层隔离而生,强行用于嵌套修改,容易埋下数据污染隐患。
只处理顶层,嵌套仍是同一块内存
展开运算符遍历对象自身可枚举属性,对基础类型(字符串、数字等)真正复制值;但遇到对象、数组等引用类型,只是把指针复制过去。比如:
const user = { name: "李四", profile: { age: 30, city: "北京" } };-
const copy = { ...user };→copy.profile和user.profile指向同一个对象 - 执行
copy.profile.city = "上海",user.profile.city也会变成 "上海"
无法自动穿透,手动展开越深越难维护
想让某一层也独立,必须显式展开对应层级:
- 改
profile.age:用{ ...user, profile: { ...user.profile, age: 31 } } - 再改
profile.address.zip:得写成{ ...user, profile: { ...user.profile, address: { ...user.profile.address, zip: "100000" } } } - 三层以上嵌套,代码迅速膨胀,易漏、难读、难测
跳过很多关键信息,不等于“完整拷贝”
展开运算符只处理自有、可枚举、非 Symbol 的属性:
- 不可枚举属性(如
Object.defineProperty(obj, 'hidden', { enumerable: false, value: 42 }))被忽略 - 原型链上的属性、Symbol 键、getter/setter 都不会被复制
- 遇到
null、undefined、函数、Date、RegExp等特殊值时行为不一致,甚至报错
什么时候该换方案
当出现以下任一情况,说明展开已不合适:
- 对象有三层及以上嵌套(如
a.b.c.d) - 需要批量更新多个嵌套路径,且不确定哪些字段会变
- 数据含函数、正则、日期、循环引用或
undefined
此时推荐:纯 JSON 数据可用 JSON.parse(JSON.stringify(obj));现代环境优先用 structuredClone(obj);生产项目常用 _.cloneDeep()。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











