浅拷贝后的嵌套数组仍指向同一块物理内存,因其仅复制外层数组容器及内部元素的引用地址,未为嵌套对象分配新堆空间,导致修改深层内容时原数组同步变化。

浅拷贝后的嵌套数组仍指向同一块物理内存,根本原因在于:它只复制了外层数组的“容器”,没复制里面元素所指向的“实际数据”。
浅拷贝只复制第一层引用地址
JavaScript 中的数组是引用类型。当你对一个含嵌套数组的对象执行浅拷贝(比如用 Object.assign、Array.from 或展开运算符 [...arr]),引擎会为外层数组创建新对象,但其中每个子项——尤其是内部的数组或对象——仍保留原来的内存地址。
- 原始数组
[[1,2], [3,4]]在堆中存着两个子数组,地址分别是@0x100和@0x200 - 浅拷贝后的新数组
[[1,2], [3,4]]是新对象,但它内部两个元素仍分别指向@0x100和@0x200 - 所以修改
newArr[0].push(5),等同于修改oldArr[0]所指的那块堆内存
内存结构上就是“共享指针”
栈中存放的是变量名和它们指向堆的地址。浅拷贝不生成新的堆空间,只是把原对象各属性的地址值复制一份到新对象里。对于嵌套数组来说,它的元素本身就是指针,这些指针被原样复制,自然还连着原来的数据块。
- 基本类型(如数字、字符串)被复制值,彼此隔离
- 引用类型(如数组、对象、函数)被复制地址,形成共用关系
- 这种机制轻量高效,但一旦修改深层内容,就突破了“副本”的预期边界
一个典型反例帮你确认问题
运行这段代码就能直观看到效果:
const original = [[1, 2], [3, 4]]; const shallow = [...original]; // 浅拷贝 shallow[0].push(99); // 修改嵌套数组 console.log(original); // 输出 [[1, 2, 99], [3, 4]] —— 原数组也被改了
这说明 shallow[0] 和 original[0] 指向的是堆中同一个数组实例,不是两个独立副本。
真正隔离需要递归处理每一层
要让嵌套数组彻底独立,必须确保每一层引用类型都分配新内存。也就是深拷贝:遍历到每个子数组或子对象时,不再复用地址,而是新建并填充内容。
- JSON.parse(JSON.stringify()) 可行但有局限(丢失函数、undefined、Date 等)
- structuredClone() 是现代标准方案,支持 Map、Set、Date、RegExp 等多数类型
- 手写递归函数可精确控制逻辑,适合复杂结构或需过滤字段的场景

















