展开运算符(...)仅对数组最外层执行浅拷贝,内部对象仍共享引用,无法处理嵌套结构;修改副本中嵌套对象会影响原数组,需用structuredclone、json序列化或手动映射等深拷贝方案。

展开运算符(...)在 JavaScript 中常被用于浅拷贝对象数组,但它无法处理嵌套对象的深层结构,导致修改副本时可能意外影响原数组。
只做一层浅拷贝
展开运算符对数组本身是浅拷贝:它复制的是数组中每个元素的引用,而非值。如果数组里存的是普通值(如数字、字符串),没问题;但若存的是对象或数组,副本和原数组仍共享同一份内存地址。
-
问题示例:
const arr = [{ name: 'Alice' }]; const copy = [...arr]; copy[0].name = 'Bob';→ 原数组arr[0].name也变成'Bob' - 本质是:展开后得到的新数组,其内部对象仍指向原对象,不是新对象
无法自动处理嵌套结构
即使多层嵌套,展开运算符也不会递归克隆。它只展开最外层数组,里面每一项仍是原引用。
- 比如
const data = [{ user: { info: { age: 25 } } }]; const cloned = [...data];→cloned[0]和data[0]是同一个对象,cloned[0].user和data[0].user也是同一个 - 想真正隔离修改,必须手动逐层深拷贝,或借助其他方法
替代方案需按需选择
是否需要深拷贝,取决于数据结构和使用场景。简单对象可用 structuredClone()(现代环境支持),兼容性要求高时可选 JSON.parse(JSON.stringify())(注意限制),或用 Lodash 的 cloneDeep。
-
structuredClone(obj):支持 Map、Set、Date、RegExp 等,但不支持函数、undefined、Symbol -
JSON.parse(JSON.stringify(obj)):会丢弃函数、undefined、循环引用、BigInt 等,仅适合纯数据对象 - 手动映射 + 展开:如
arr.map(item => ({ ...item }))可浅拷贝每项对象,但仅限一级嵌套
实际开发中的判断建议
不必一概否定展开运算符,关键看后续操作是否会修改内部对象。
- 只读遍历或生成新对象(如
map返回全新对象)→ 安全使用展开 - 需修改副本中对象字段 → 必须配合对象展开或深拷贝
- 不确定结构深度或含特殊类型 → 避免依赖展开,优先选明确语义的克隆方式
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











