展开运算符{...obj}仅浅拷贝第一层可枚举属性:基础类型独立复制,引用类型共享内存地址;不可枚举属性、Symbol键、原型链均被忽略;嵌套修改会相互影响,深拷贝需用structuredClone()等替代方案。

展开运算符({...obj})克隆对象时,只作用于第一层属性,无法穿透嵌套结构。它不是“不够深”,而是设计上就只做单层复制——这是它的能力边界,不是 bug。
它到底复制了什么
展开运算符遍历源对象自身的、可枚举的属性(即 Object.keys() 能拿到的),逐个赋值到新对象中:
- 基础类型(字符串、数字、布尔值、null、undefined)被真正复制,彼此独立
- 引用类型(对象、数组、函数、Date、RegExp 等)只复制内存地址,新旧对象共享同一底层数据
- 不可枚举属性、原型链上的方法、Symbol 键全部被忽略
嵌套修改会互相影响
只要修改发生在第二层及更深的位置,原始对象和拷贝对象就会同步变化:
-
copy.name = "新名字"→ 不影响原对象(顶层基础类型) -
copy.profile.age = 30→ 原对象的profile.age同步变成 30(profile是引用) -
copy.items.push("x")→ 原对象items数组也多出一项
手动“展开到下一层”可行但不 scalable
如果只想隔离某一层嵌套,可以显式展开对应层级:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 改
profile.name:用{ user, profile: { ...user.profile, name: "新名字" } } - 再改
profile.address.city:需继续写成{ user, profile: { ...user.profile, address: { ...user.profile.address, city: "杭州" } } } - 三层以上嵌套会让代码迅速变得冗长、易错、难维护
什么时候该换方案
遇到以下情况,说明展开运算符已超出适用范围:
- 对象嵌套达到三层或更深(如
a.b.c.d) - 需要保证任意层级修改都不影响原始数据
- 对象中含
Date、RegExp、Map、Set或循环引用 - 业务逻辑涉及批量更新多个深层路径,且不确定哪些字段会被动
此时推荐使用 structuredClone()(现代浏览器)、_.cloneDeep()(Lodash),或在简单纯数据场景下用 JSON.parse(JSON.stringify(obj))(注意类型限制)。

















