如何实现深拷贝

深度拷贝(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;
}

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

相关推荐
大鸡爪2 小时前
《Vue3 组件库搭建指北:pnpm + monorepo + 代码提交规范+ BEM 环境配置》
前端·vue.js
UIUV2 小时前
TypeScript深度学习笔记:从动态语言到强类型工程化实践
前端·typescript
代码猎人2 小时前
箭头函数与普通函数有哪些区别
前端
Lazy_zheng2 小时前
一文搞懂 JavaScript 数据类型转换(显式 & 隐式全解析)
前端·javascript·面试
小宇的天下2 小时前
Virtuoso 中的tech file 详细说明
java·服务器·前端
Zoey的笔记本2 小时前
「软件开发缺陷管理工具」的闭环追踪设计与自动化集成架构
java·服务器·前端
Sapphire~2 小时前
【前端基础】04-XSS(跨站脚本攻击,Cross-Site Scripting)
前端·xss
奔跑的web.2 小时前
Vue 3.6 重磅新特性:Vapor Mode 深度解析
前端·javascript·vue.js
MediaTea2 小时前
Python OOP 设计思想 13:封装服务于演化
linux·服务器·前端·数据库·python