展开运算符仅支持一层浅拷贝,无法自动处理多层嵌套对象;它复制基础类型值,但对嵌套对象、数组等引用类型只复制指针,导致深层属性仍共享内存地址,修改会相互影响。

展开运算符本身只做一层浅拷贝,无法自动处理多层嵌套对象。所谓“多层对象浅拷贝”,其实是误解——它不支持多层,也不提供递归能力,所有嵌套结构(如 user.profile.address)仍共享原始引用。
为什么不能靠展开实现多层拷贝
展开运算符复制的是对象第一层可枚举属性的值:基础类型(字符串、数字等)被真正复制;引用类型(对象、数组、函数)只复制指针,内存地址不变。
-
{ user }→user.profile和copy.profile指向同一对象 - 修改
copy.profile.name会同步影响user.profile.name - 即使写成
{ ...user, profile: { ...user.profile } },也只是手动展开到第二层,第三层(如profile.address)依然未隔离
想安全修改嵌套字段?得逐层展开
如果目标是让某一层嵌套也独立,必须显式展开对应层级,不能依赖一次展开自动穿透:
- 改
user.profile.name:用{ user, profile: { ...user.profile, name: "新名字" } } - 再改
profile.address.city:需继续展开下一层{ ...user.profile, address: { ...user.profile.address, city: "杭州" } } - 嵌套越深,手动展开越繁琐,容易遗漏;三层以上通常建议换深拷贝方案
什么情况下该放弃展开,选其他方式
当需要真正隔离整个嵌套结构时,展开运算符就不是合适工具:
- 数据含三层及以上嵌套(如
a.b.c.d) - 要批量更新多个嵌套路径,且不确定哪些会被修改
- 对象含函数、
Date、RegExp、undefined或循环引用
此时可考虑:
– 简单纯数据:用 JSON.parse(JSON.stringify(obj))(注意兼容性限制)
– 生产环境推荐:structuredClone(obj)(现代浏览器支持)或 _.cloneDeep()
小结:展开适合什么场景
它最擅长的是顶层字段的快速复制、覆盖与组合——比如表单提交前临时修改几个字段、合并配置对象、提取并重命名关键属性。只要不碰嵌套对象的内部值,它既安全又高效。一旦涉及深层修改,就得明确知道“自己负责展开到哪一层”,不能指望它自动递归。











