JavaScript 深拷贝

定义

深拷贝:完全复制对象及所有嵌套结构,新旧对象相互独立(修改新对象不影响原对象)。

一、基础方法(简单场景)

1. JSON.parse(JSON.stringify())

  • 用法:

    const deepCopy = JSON.parse(JSON.stringify(original));
    
  • 优点:简单易用,无需依赖。先转换为json格式,再转换为js对象。

  • 局限性:

    • 不支持 FunctionRegExpDateMapSet 等特殊类型;
    • 忽略 undefinedSymbol 属性;
    • 循环引用(如 obj.self = obj)会报错;
    • 不拷贝原型链。

二、手动递归实现(灵活可控)

1. 基础版(处理对象 / 数组)

function deepClone(target) {
  if (target === null || typeof target !== 'object') return target;
  const cloneTarget = Array.isArray(target) ? [] : {};
  for(const key in target){
    cloneTarget[key] = deepClone(target[key]);
  }};
  return cloneTarget;
}

2. 增强版(解决循环引用 + 特殊类型)

通过weakMap缓存已经处理/拷贝过的对象,添加一层判断是否已经拷贝过,如果拷贝过直接返回

function deepClone(target, map = new WeakMap()) {
  if (target === null || typeof target !== 'object') return target;
  if (map.has(target)) return map.get(target); // 解决循环引用

  let cloneTarget;
  // 处理 Date/RegExp 等特殊类型
  if (target instanceof Date) cloneTarget = new Date(target);
  else if (target instanceof RegExp) cloneTarget = new RegExp(target.source, target.flags);
  else cloneTarget = Array.isArray(target) ? [] : {};

  map.set(target, cloneTarget);
  Reflect.ownKeys(target).forEach(key => {
    cloneTarget[key] = deepClone(target[key], map);
  });
  return cloneTarget;
}

三、工具库(生产环境推荐)

1. Lodash _.cloneDeep()

import _ from 'lodash';
const deepCopy = _.cloneDeep(original);
  • 支持几乎所有类型(MapSetSymbol 等),处理循环引用。

2. Ramda clone

import { clone } from 'ramda';
const deepCopy = clone(original);

四、JS原生 API(现代浏览器/Node.js)

1. structuredClone()

const original = {
  date: new Date(),
  reg: /pattern/gi,
  map: new Map([['key', 'value']]),
  set: new Set([1, 2, 3])
};

const deepCopy = structuredClone(original);

特性:

  • ✅ 支持复杂类型(DateRegExpMapSetArrayBuffer 等)
  • ✅ 处理循环引用
  • ✅ 浏览器原生实现,性能优秀
  • ❌ 不支持函数、DOM 元素、原型链

适用场景:

  • 需要完整保留复杂类型的深拷贝
  • 状态管理中的状态快照
  • 高性能深拷贝需求

五、注意事项

  • 循环引用:需用 WeakMap 缓存已拷贝对象,避免递归死循环;
  • 原型链:默认不拷贝,需手动处理(如 Object.setPrototypeOf);
  • 特殊对象BlobDOM 元素等需调用对应构造函数复制。

场景选择

  • 简单场景:JSON.parse(JSON.stringify())
  • 复杂场景:Lodash _.cloneDeep() 或增强版手动实现。

六、浅拷贝

← 返回列表