深拷贝

深拷贝的定义、常见实现方式和与浅拷贝、structuredClone 的关系。

#type / concept #status / growing #tech / dev / frontend

[!info] related notes

深拷贝

一句话定义

深拷贝递归复制对象的所有层级引用,产出的新对象与原对象完全独立,修改任一方不影响另一方。

核心机制

浅拷贝只复制第一层:嵌套对象仍然是共享引用。深拷贝遍历每一层,遇到对象/数组就递归创建新副本。

原对象:  { a: 1, b: { c: 2 } }
浅拷贝:  { a: 1, b: ──┐ }
                       └──→ { c: 2 }  (同一个!)
深拷贝:  { a: 1, b: ──┐ }
                       └──→ { c: 2 }  (独立副本)

方法一:JSON.parse + JSON.stringify

const original = { name: 'Alice', scores: [90, 85], meta: { age: 25 } };
const cloned = JSON.parse(JSON.stringify(original));

cloned.scores.push(100);
console.log(original.scores); // [90, 85] — 不受影响

局限性

  • 丢失 undefinedFunctionSymbol 键值
  • Date 变为 ISO 字符串(不再是 Date 实例)
  • RegExpMapSet 变为空对象 {}
  • 循环引用直接报错 TypeError: Converting circular structure to JSON
  • BigInt 直接报错 TypeError: Do not know how to serialize a BigInt

方法二:structuredClone(推荐)

const original = {
  date: new Date(),
  data: new Map([['key', 'value']]),
};
const cloned = structuredClone(original);

console.log(cloned.date instanceof Date);  // true
console.log(cloned.data instanceof Map);   // true

优点:原生支持 DateRegExpMapSetArrayBufferBlob、循环引用。 局限:不能克隆 FunctionDOM 节点、Proxy,会抛出 DataCloneError

方法三:手写递归

function deepClone(obj, seen = new WeakMap()) {
  // 基本类型 / null
  if (obj === null || typeof obj !== 'object') return obj;

  // 循环引用
  if (seen.has(obj)) return seen.get(obj);

  // Date
  if (obj instanceof Date) return new Date(obj);
  // RegExp
  if (obj instanceof RegExp) return new RegExp(obj);
  // Map
  if (obj instanceof Map) {
    const map = new Map();
    seen.set(obj, map);
    obj.forEach((v, k) => map.set(deepClone(k, seen), deepClone(v, seen)));
    return map;
  }
  // Set
  if (obj instanceof Set) {
    const set = new Set();
    seen.set(obj, set);
    obj.forEach(v => set.add(deepClone(v, seen)));
    return set;
  }

  // Array / Object
  const clone = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
  seen.set(obj, clone);
  for (const key of Reflect.ownKeys(obj)) {
    clone[key] = deepClone(obj[key], seen);
  }
  return clone;
}

性能对比

方法循环引用类型支持性能环境要求
JSON.parse/stringify报错基本类型 + 数组 + 普通对象所有环境
structuredClone支持多数内建类型中等浏览器 / Node 17+
手写递归需手动处理可自定义慢(深度大时)所有环境
lodash _.cloneDeep支持最全面中等需引入依赖

边界与常见误解

  • JSON 方案”够用”是有条件的:如果数据中只有基本类型和普通对象/数组,JSON 方案最快且最简洁。
  • structuredClone 不是万能的:函数、DOM 节点、类实例上的方法全部丢失。
  • 性能:对大对象,JSON.parse(JSON.stringify()) 通常比 structuredClone 快 2-5 倍,因为 JSON 引擎是 C++ 实现的。
  • 类实例structuredClone 和 JSON 方案都只拷贝数据,不保留原型链上的方法。
创建于 2026/4/9 更新于 2026/5/27