如何用javascript实现深拷贝与浅拷贝【教程】

10次阅读

javaScript无万能深拷贝函数;jsON.parse(json.stringify())会丢失函数、undefined等且不支持循环引用;浅拷贝如{…obj}仅复制第一层,引用类型仍共享内存;手写深拷贝需递归+Weakmap防循环;生产环境推荐lodash.cloneDeep或structuredClone。

如何用javascript实现深拷贝与浅拷贝【教程】

javascript 里没有“一键深拷贝”的万能函数,JSON.parse(JSON.stringify(obj)) 看似简单,但会丢函数、undefined、symbol、循环引用、dateregexp 等;而浅拷贝用 Object.assign() 或展开运算符 {...obj} 就行,但只管第一层。

浅拷贝:只复制对象第一层属性值

浅拷贝本质是让新对象和原对象的顶层属性指向相同的内存地址(对引用类型而言)。常见写法有:

  • Object.assign({}, obj) —— 注意第一个参数必须是空对象,否则会污染目标对象
  • {...obj} —— 仅适用于对象字面量,不支持 class 实例或原型链上的属性
  • Array.from(arr)[...arr] —— 数组浅拷贝最常用,但嵌套数组仍共享子项引用

典型陷阱:const a = { x: 1, y: { z: 2 } }; const b = { ...a }; b.y.z = 99; console.log(a.y.z); // 输出 99 —— 因为 y 是引用,没被真正复制。

深拷贝:递归复制所有层级的可遍历属性

手写深拷贝需判断类型、避开循环引用、处理特殊对象。一个最小可用版本如下:

立即学习Java免费学习笔记(深入)”;

function deepClone(obj, hash = new WeakMap()) {   if (obj === null || typeof obj !== 'object') return obj;   if (hash.has(obj)) return hash.get(obj);      const cloned = Array.isArray(obj) ? [] : {};   hash.set(obj, cloned);      for (const key in obj) {     if (Object.prototype.hasOwnProperty.call(obj, key)) {       cloned[key] = deepClone(obj[key], hash);     }   }   return cloned; }

关键点:

  • WeakMap 记录已克隆的对象,防止循环引用导致溢出
  • 必须用 Object.prototype.hasOwnProperty.call() 过滤原型链属性
  • 不处理 DateRegExpMapSet 等内置类型,如需支持得额外分支判断

为什么不用 JSON.parse(JSON.stringify(obj))

它看似简洁,但实际限制极多:

  • undefinedfunctionSymbol 字段会被直接忽略
  • Date 变成字符串RegExp 变成空对象 {}
  • 遇到循环引用直接抛错:TypeError: Converting circular structure to JSON
  • 不能保留原型链,所有结果都是纯 Object 实例

仅适合临时处理“干净”的纯数据对象(比如 API 返回的扁平 JSON 数据)。

现代项目中更推荐的方案

生产环境别自己造轮子,优先用成熟库:

  • Lodash 的 _.cloneDeep() —— 兼容性好、覆盖类型全、性能经过优化
  • 结构化克隆(chrome 98+ / firefox 94+):structuredClone(obj) —— 原生支持 DateMapSetArrayBuffer 等,但暂不支持函数和 undefined

注意:structuredClonenode.js 17.0+ 才可用,且不支持 Error 对象和某些自定义类实例 —— 这些边界情况,往往才是深拷贝真正难搞的地方。

text=ZqhQzanResources