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

10次阅读

javaScript浅拷贝只复制第一层属性引用,深拷贝需递归处理所有层级;jsON.parse(json.stringify())因丢失函数、undefined等类型而不适用于生产环境。

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

javascript 中的浅拷贝只复制对象第一层属性的引用,深拷贝则递归复制所有层级——但直接用 JSON.parse(JSON.stringify(obj)) 会丢函数、undefined、symboldateregexp 等,生产环境必须避开这个坑。

浅拷贝:哪些方法只复制一层?

浅拷贝本质是“新对象 + 原对象顶层属性值的赋值”,对嵌套对象仍共享内存地址。

  • Object.assign({}, obj):忽略原型链上的属性,不处理 getter/setter
  • {...obj}(展开语法):同上,且无法拷贝不可枚举属性
  • Array.prototype.slice()[...arr]:仅适用于数组,对嵌套数组元素仍是浅拷贝

常见错误现象:const a = {x: {y: 1}}; const b = {...a}; b.x.y = 2; 执行后 a.x.y 也变成 2。

深拷贝:为什么 JSON.parse(JSON.stringify()) 不可靠?

它会跳过 undefinedfunctionSymbol,把 Date 变成字符串RegExp 变成空对象,NaN 变成 NULL,遇到循环引用直接报错 TypeError: Converting circular structure to JSON

适用场景仅限纯数据对象(POJO),且确认不含上述类型。例如配置项、API 响应体预处理可临时用,但不能用于状态管理或通用工具函数。

手写简单深拷贝:如何处理常见边界类型?

核心思路是类型判断 + 递归,同时用 Weakmap 解决循环引用问题。

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

这个版本能处理数组、普通对象、null、基本类型,支持循环引用;但依然不处理 DateRegExpMapSet 等——如果业务需要,得在判断分支里单独处理,比如:

  • 遇到 obj instanceof Date → 返回 new Date(obj)
  • 遇到 obj instanceof RegExp → 返回 new RegExp(obj)
  • 遇到 obj instanceof Map → 遍历 obj.entries() 重建

Lodash 的 cloneDeep 和结构化克隆 API 怎么选?

现代浏览器已支持 structuredClone()chrome 98+、firefox 94+、safari 15.4+),它能正确处理 DateRegExpMapSetArrayBufferTypedArray,也支持循环引用,且比手写快。但它不支持函数、undefinedSymbol,且 node.js 直到 v18.13.0 才默认启用(需加 --enable-structured-clone 标志)。

Lodash 的 cloneDeep 兼容性更好,但体积大(约 10KB min+gzip),若项目已引入 Lodash 可直接用;否则优先考虑 structuredClone + 特殊类型 fallback。

真正容易被忽略的是:深拷贝永远有成本。高频调用时,先想清楚是否真需要拷贝——有时用不可变更新(如 Immer)、状态分片或引用隔离更合适。

text=ZqhQzanResources