javascript如何实现深拷贝和浅拷贝?【教程】

浅拷贝只复制第一层引用,Object.assign和展开运算符最常用;深拷贝需递归处理,JSON方法快但限制多,structuredClone更可靠但有兼容性要求,lodash.cloneDeep功能全但体积大。

浅拷贝只复制第一层引用,Object.assign 和展开运算符最常用

浅拷贝不会递归复制嵌套对象,只是把顶层属性的值(如果是引用类型)复制一份新指针。这意味着修改嵌套对象的属性,原对象和拷贝对象会互相影响。

常见写法:

  • Object.assign({}, obj):适用于普通对象,不处理 nullundefined 作为源
  • {...obj}:ES6+ 更简洁,但同样只展开一层,且不能拷贝不可枚举属性、Symbol 键或原型链上的属

  • Array.prototype.slice()[...arr]:数组浅拷贝,对嵌套数组无效

注意:Object.assign 会跳过 undefined 源对象,但不会报错;如果目标对象有 setter,可能触发副作用。

深拷贝要递归处理所有层级,JSON.parse(JSON.stringify()) 最快但限制多

这个组合在多数简单场景下够用,执行快、写法短,但它根本无法处理以下情况:

  • undefinedfunctionSymbolBigInt 会被忽略或报错
  • 循环引用直接抛出 TypeError: Converting circular structure to JSON
  • Date、RegExp、Map、Set、TypedArray 等对象变成空对象或丢失类型
  • 原型链和不可枚举属性全部丢失

所以它只适合「纯数据对象」——即只含字符串、数字、布尔、null、普通对象和数组的结构。

需要可靠深拷贝时,用 structuredClone(现代浏览器)或手写递归函数

structuredClone 是目前最接近“开箱即用”的标准方案,支持 DateRegExpMapSetArrayBuffer 等,也支持循环引用,但仍有边界:

  • 不支持 functionundefinedSymbolPromiseWindow 等宿主对象
  • Node.js 17+ 才默认启用,旧版需加 --experimental-structured-cloning
  • 浏览器兼容性:Chrome 98+、Firefox 94+、Safari 15.4+,IE 完全不支持

若需兼容更老环境或定制行为(比如跳过某些字段、转换日期格式),就得手写递归函数。关键点是:必须用 Map 记录已访问对象来处理循环引用,否则栈溢出;区分 ArrayDateRegExp 等类型并分别构造新实例。

第三方库如 lodash.cloneDeep 稳定但引入体积大

lodash.cloneDeep 覆盖场景最广,能处理函数(拷贝为 undefined)、Map/Set、稀疏数组、甚至部分 DOM 节点(取决于版本)。但它不是零依赖:压缩后约 10KB,对轻量项目可能是负担。

使用前确认是否真需要它支持的所有类型——比如项目里压根没 Map 或循环引用,那 structuredClone 或改良版 JSON 方案就更轻量。

另外,别在热路径(如每帧调用)中无节制深拷贝大对象,性能损耗明显;考虑用不可变更新(immer)或状态分片来规避。