如何实现深拷贝

深度拷贝(Deep Copy)实现详解

深度拷贝创建一个完全独立的新对象,递归复制原始对象的所有嵌套对象,使得新旧对象完全独立,互不影响。

1. JavaScript/TypeScript 实现

方法一:JSON方法(最简单但有局限性)

js 复制代码
const original = { a: 1, b: { c: 2, d: [3, 4] } };
const deepCopy = JSON.parse(JSON.stringify(original));

局限性

  • 不能复制函数、undefined、Symbol
  • 不能处理循环引用
  • 会忽略原型链
  • Date对象会变成字符串

方法二:递归实现(最完整)

js 复制代码
function deepClone(obj, hash = new WeakMap()) {
  // 处理基本类型和null
  if (obj === null || typeof obj !== 'object') return obj;
  
  // 处理Date对象
  if (obj instanceof Date) return new Date(obj);
  
  // 处理RegExp对象
  if (obj instanceof RegExp) return new RegExp(obj);
  
  // 处理Array对象
  if (Array.isArray(obj)) {
    const arrCopy = [];
    hash.set(obj, arrCopy); // 存储已拷贝的对象,处理循环引用
    obj.forEach((item, index) => {
      arrCopy[index] = deepClone(item, hash);
    });
    return arrCopy;
  }
  
  // 处理普通对象
  if (typeof obj === 'object') {
    // 如果已经拷贝过,直接返回(处理循环引用)
    if (hash.has(obj)) return hash.get(obj);
    
    const objCopy = {};
    hash.set(obj, objCopy);
    
    // 复制所有属性,包括Symbol属性
    const allKeys = [
      ...Object.getOwnPropertyNames(obj),
      ...Object.getOwnPropertySymbols(obj)
    ];
    
    for (const key of allKeys) {
      objCopy[key] = deepClone(obj[key], hash);
    }
    
    return objCopy;
  }
  
  // 其他情况(如函数)直接返回
  return obj;
}

// 使用示例
const original = {
  name: 'Alice',
  scores: [85, 90, 95],
  address: {
    city: 'Beijing',
    street: 'Main St'
  },
  getInfo() {
    return `${this.name} from ${this.address.city}`;
  },
  date: new Date(),
  regex: /test/gi
};

original.self = original; // 循环引用

const deepCopy = deepClone(original);

方法三:使用structuredClone API(现代浏览器)

js 复制代码
// 支持大多数类型,包括循环引用
const original = { a: 1, b: { c: 2 } };
const deepCopy = structuredClone(original);

// 检查是否支持
if (typeof structuredClone === 'function') {
  // 可以使用
}

方法四:使用第三方库

js 复制代码
// lodash
import _ from 'lodash';
const deepCopy = _.cloneDeep(original);

// Ramda
import * as R from 'ramda';
const deepCopy = R.clone(original);

2. 不同场景下的深拷贝选择

性能对比(JavaScript示例)

js 复制代码
// 测试不同方法的性能
const testObj = {
  data: Array(1000).fill(0).map((_, i) => ({
    id: i,
    nested: { value: Math.random() },
    arr: Array(10).fill(Math.random())
  }))
};

// JSON方法(最快,但有局限性)
console.time('JSON');
const copy1 = JSON.parse(JSON.stringify(testObj));
console.timeEnd('JSON');

// 递归方法
console.time('Recursive');
const copy2 = deepClone(testObj);
console.timeEnd('Recursive');

// structuredClone
console.time('structuredClone');
const copy3 = structuredClone(testObj);
console.timeEnd('structuredClone');

选择指南

方法 优点 缺点 适用场景
JSON方法 简单、快速 丢失函数、特殊对象 纯数据对象,无特殊类型
递归实现 最完整,可扩展 实现复杂,性能较低 需要完整复制,包含特殊类型
structuredClone 原生支持,性能好 浏览器兼容性 现代Web应用
第三方库 稳定,功能丰富 增加依赖 企业级应用

3. 深拷贝的注意事项

  1. 循环引用处理:必须检测并处理循环引用
  2. 特殊对象类型:Date、RegExp、Map、Set、函数等
  3. 原型链继承:是否保持原型链
  4. 不可枚举属性:是否复制不可枚举属性
  5. Symbol属性:是否复制Symbol作为键的属性
  6. 性能优化:对于大对象,需要考虑性能问题

4. 最佳实践

js 复制代码
// 生产环境推荐:使用经过测试的库
// 对于简单需求:JSON.parse(JSON.stringify(obj))
// 对于复杂需求:使用lodash的_.cloneDeep或自己实现完整递归

// 安全考虑:处理不可信数据时的深拷贝
function safeDeepClone(obj) {
  // 1. 验证对象大小
  if (JSON.stringify(obj).length > 10 * 1024 * 1024) { // 10MB限制
    throw new Error('Object too large for deep cloning');
  }
  
  // 2. 使用JSON方法(避免原型链攻击)
  const cloned = JSON.parse(JSON.stringify(obj));
  
  // 3. 验证结果
  // ... 这里可以添加验证逻辑
  
  return cloned;
}

深拷贝是实现数据隔离的重要手段,选择合适的方法需要根据具体需求、性能要求和环境限制来决定。对于大多数应用场景,使用成熟的第三方库是最稳妥的选择。

相关推荐
VT.馒头14 分钟前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript
css趣多多25 分钟前
一个UI内置组件el-scrollbar
前端·javascript·vue.js
C澒1 小时前
前端整洁架构(Clean Architecture)实战解析:从理论到 Todo 项目落地
前端·架构·系统架构·前端框架
C澒1 小时前
Remesh 框架详解:基于 CQRS 的前端领域驱动设计方案
前端·架构·前端框架·状态模式
Charlie_lll1 小时前
学习Three.js–雪花
前端·three.js
onebyte8bits1 小时前
前端国际化(i18n)体系设计与工程化落地
前端·国际化·i18n·工程化
C澒1 小时前
前端分层架构实战:DDD 与 Clean Architecture 在大型业务系统中的落地路径与项目实践
前端·架构·系统架构·前端框架
BestSongC1 小时前
行人摔倒检测系统 - 前端文档(1)
前端·人工智能·目标检测
0思必得02 小时前
[Web自动化] Selenium处理滚动条
前端·爬虫·python·selenium·自动化
Misnice2 小时前
Webpack、Vite、Rsbuild区别
前端·webpack·node.js