登录
首页 >  文章 >  前端

拷贝陷阱:浅拷贝嵌套数组为何共用内存

时间:2026-05-28 14:27:49 400浏览 收藏

浅拷贝看似创建了数组副本,实则只是复制了外层容器和内部引用地址,导致嵌套的数组或对象仍共享同一块堆内存——修改新数组的深层元素会意外改变原数组,这种“指针共享”陷阱常让开发者误以为拷贝已完全隔离;要真正实现数据独立,必须采用深拷贝策略,如 structuredClone() 或递归遍历,为每一层引用类型分配全新内存空间。

如何理解 拷贝陷阱:为什么浅拷贝后的嵌套数组依然指向同一块原始物理内存

浅拷贝后的嵌套数组仍指向同一块物理内存,根本原因在于:它只复制了外层数组的“容器”,没复制里面元素所指向的“实际数据”。

浅拷贝只复制第一层引用地址

JavaScript 中的数组是引用类型。当你对一个含嵌套数组的对象执行浅拷贝(比如用 Object.assignArray.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 等多数类型
  • 手写递归函数可精确控制逻辑,适合复杂结构或需过滤字段的场景

终于介绍完啦!小伙伴们,这篇关于《拷贝陷阱:浅拷贝嵌套数组为何共用内存》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布文章相关知识,快来关注吧!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>