如何在 JavaScript 中进行深度克隆?

在 JavaScript 中进行深度克隆(deep clone)是指创建一个对象的完整副本,并且副本中所有的嵌套对象也被复制,而不是只是引用原始对象中的嵌套对象。深度克隆与浅克隆的主要区别在于,浅克隆只复制对象的引用,而深度克隆会递归复制对象中所有层级的数据。

为什么需要深度克隆?

在一些复杂的应用中,尤其是涉及到不可变数据结构时,我们需要对对象进行修改而不影响原始对象。比如在 React 中的状态管理,Vue 中的数据绑定,或者 Redux 中的状态更新。

常见的深度克隆方法

方法 1:使用 JSON 方法

最常见的实现深度克隆的方式是通过 JSON.parse()JSON.stringify() 来实现。这种方法非常简便,但它有一些限制和潜在的缺陷:

const originalObj = {name: 'Alice',address: {city: 'Wonderland',postalCode: '12345'}
};const clonedObj = JSON.parse(JSON.stringify(originalObj));console.log(clonedObj); // { name: 'Alice', address: { city: 'Wonderland', postalCode: '12345' } }

优点:

  • 简单易懂,代码很简洁。

缺点:

  • 无法克隆函数、undefinedSymbolDate 等类型。
  • 会丢失对象中的循环引用,无法处理循环结构。
  • 对象中的 prototype 链、getter/setter 等特殊属性会被忽略。
方法 2:手动递归克隆

对于需要处理更复杂结构的对象,可以实现一个递归的深度克隆函数:

function deepClone(obj) {if (obj === null || typeof obj !== 'object') {return obj; // 如果是原始类型,直接返回}// 处理特殊情况:Date 和 RegExp 等类型if (obj instanceof Date) {return new Date(obj); // 克隆 Date 对象}if (obj instanceof RegExp) {return new RegExp(obj); // 克隆 RegExp 对象}// 处理数组和对象const clonedObj = Array.isArray(obj) ? [] : {};for (const key in obj) {if (obj.hasOwnProperty(key)) {clonedObj[key] = deepClone(obj[key]); // 递归克隆每个属性}}return clonedObj;
}const originalObj = {name: 'Bob',age: 30,dateOfBirth: new Date('1994-12-25'),address: {city: 'New York',zip: '10001'},hobbies: ['reading', 'coding'],greet: function() { console.log('Hello'); }
};const clonedObj = deepClone(originalObj);console.log(clonedObj);
console.log(clonedObj.dateOfBirth instanceof Date); // true
console.log(clonedObj.hobbies === originalObj.hobbies); // false

优点:

  • 支持多种数据类型(包括 DateRegExp)。
  • 能够处理循环引用(需要额外的处理)。
  • 可以根据需要定制。

缺点:

  • 需要手动处理不同类型的对象,代码复杂。
  • 如果对象中有循环引用,可能会导致栈溢出。
方法 3:使用 StructuredClone (现代浏览器支持)

StructuredClone 是一种浏览器原生支持的方法,能够克隆对象及其内部结构,包括 DateMapSetArrayBuffer 等。但它仍然不支持函数或某些特殊对象(如 RegExp)。

const originalObj = {name: 'Charlie',birthdate: new Date('1992-05-15'),hobbies: ['running', 'painting'],meta: new Map([['key', 'value']])
};const clonedObj = structuredClone(originalObj);console.log(clonedObj);
console.log(clonedObj.birthdate instanceof Date); // true
console.log(clonedObj.meta instanceof Map); // true
console.log(clonedObj.hobbies === originalObj.hobbies); // false

优点:

  • 现代浏览器内建支持,代码简洁。
  • 支持更多的数据类型(如 MapSetArrayBufferDate 等)。

缺点:

  • 并非所有 JavaScript 环境都支持 structuredClone,例如在一些老版本的浏览器或 Node.js 中不支持。
方法 4:使用第三方库

第三方库通常提供了非常强大且可靠的深度克隆功能,例如 LodashcloneDeep 方法。它可以处理大多数复杂情况,包括循环引用。

// 使用 lodash
import cloneDeep from 'lodash/cloneDeep';const originalObj = {name: 'David',hobbies: ['sports', 'music'],meta: new Map([['key', 'value']])
};const clonedObj = cloneDeep(originalObj);console.log(clonedObj);
console.log(clonedObj.hobbies === originalObj.hobbies); // false

优点:

  • 处理复杂数据结构(包括循环引用、MapSet 等)。
  • 经过广泛测试,稳定可靠。

缺点:

  • 需要引入第三方库,增加项目依赖。

实际项目中的应用

假设你在开发一个应用,并且需要对某个对象的状态进行深度克隆,例如在 Redux 或 Vuex 中管理状态:

// 假设这是你的状态对象
const state = {user: {name: 'Alice',preferences: {theme: 'dark',language: 'en',},},isAuthenticated: true,
};// 使用深度克隆来避免直接修改原始状态
const newState = deepClone(state);// 修改新的 state,不会影响原始 state
newState.user.preferences.theme = 'light';console.log(state.user.preferences.theme); // 'dark'
console.log(newState.user.preferences.theme); // 'light'

在这个例子中,深度克隆确保我们不会改变原始的 state 对象,从而避免不必要的副作用。这对于管理应用状态特别重要,尤其是在状态不可变的情况下。

总结

  • JSON 方法适用于简单对象的深度克隆,但不支持函数、日期、正则表达式等。
  • 手动递归克隆是最灵活的方式,可以处理多种类型,但需要编写额外的代码。
  • **structuredClone**是现代浏览器中的原生方法,支持更多的数据类型,但兼容性较差。
  • **第三方库(如 Lodash)**提供了可靠的深度克隆实现,适用于复杂应用,但增加了项目依赖。

在实际项目中,根据需求选择适合的方法。如果你的项目依赖较少,且需要处理复杂数据类型,手动实现或者使用 structuredClone 是不错的选择。如果是处理非常复杂的对象并且你愿意引入外部依赖,使用 Lodash 等库会更为方便。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/63556.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 关于加密技术以及应用(一)

在 开发过程中,加密是一个常见的需求,数字签名和验证、网络通信安全、数据加密解密、用于保护数据的安全性和隐私。如几种常用的加密技术AES、SSL/TLS、RSA、HMAC 、SHA等,都是我们开发过程中常用到的加密方式,只不过每一个加密方…

Python 爬虫 (1)基础 | XHR

一、XHR 1、概念 XHR,全称XMLHttpRequest,是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。它允许网页的JavaScript代码与服务器进行异步通信,即在发送请求后,浏览器不会阻塞用户的后续操作&#xff0…

二进制部署Prometheus+grafana+alertmanager+node_exporter

Prometheus 是一个开源的监控和告警工具包,旨在提供高可靠性和可扩展性。它最初由 SoundCloud 开发,现已成为云原生计算基金会(CNCF)的一部分。以下是 Prometheus 的一些关键特性和概念: 1. **时间序列数据库**&#…

工业智能网关如何为企业实现智能制造赋能?

在数字化转型的浪潮中,工业智能网关作为连接物理世界与数字世界的桥梁,正逐步成为智能制造领域的核心组件。本文将通过一个实际使用案例,深入剖析工业智能网关如何助力企业实现生产流程的优化、数据的高效采集与分析,以及智能化决…

算法设计6_随机化算法

随机化算法 随机算法的随机性(基本特征) – 对于同一实例的多次执行, 效果可能完全不同 – 时间复杂性的一个随机变量 – 解的正确性和准确性也是随机的 数值随机化算法 随机数值算法 – 主要用于数值问题求解 – 算法的输出往往是近似解 – 近似…

使用mmdeploy框架C++预测mask并绘制最小外接矩形

目录 解决目标 逻辑思路 代码实现 第1部分 第2部分 解决目标 这段代码实现了,一个基于深度学习的图像检测程序。它使用mmdeploy框架,加载一个预训练的模型【实例分割模型】来检测图像中的物体。 逻辑思路 程序首先加载模型,然后&#…

Java --- JVM编译运行过程

目录 一.Java编译与执行流程: 二.编译过程: 1.编译器(javac): 2.字节码文件(.class): 三.执行过程: 1.启动JVM(Java虚拟机): 2…

【Makefile】编译日志之输出重定向符号 >

用法1 make all >& compilelog.txt make all > compilelog.txt这两个编译命令在功能上有一些细微的区别,主要在于标准输出和标准错误的处理方式。 make all >& compilelog.txt 这个命令会将标准输出(stdout)和标准错误&a…

使用 `typing_extensions.TypeAlias` 简化类型定义:初学者指南

使用 typing_extensions.TypeAlias 简化类型定义:初学者指南 什么是 TypeAlias?安装 typing_extensions示例代码:如何使用 TypeAlias示例 1:为简单类型定义别名示例 2:为复杂类型定义别名示例 3:结合 Union…

11.关于vim编辑器的简单配置

1. 说明 在linux系统中编辑文件内容的方式有很多种,比如直接在系统中暗中某些IDE,方便快捷,也可以直接在windows系统中编辑好文件后上传到linux系统中,这些方式对于编写内容较多的文件或者整个项目的文件还是非常适合的。不过有时…

qq空间管理小助手教程代码演示

headers {"Authorization": "Bearer YOUR_ACCESS_TOKEN","Content-Type": "application/json" }# 发布说说示例( def post_moment(content):url "https://qzone-api.example.com/post_moment"data {"con…

pytest 的简单介绍

官方文档:pytest 文档 1. pytest 概述 pytest 是一个功能强大的 Python 测试框架,旨在简化单元测试和功能测试的编写与执行。它不仅支持简单的单元测试,还能进行更复杂的测试,如数据驱动的测试、并发测试等。pytest 支持自动化发…

【ArcGIS微课1000例】0133:二维建筑物依据高度生成三维模型

拓展阅读:【ArcGIS Pro微课1000例】0032:创建具有指定高程Z值的矢量数据 文章目录 一、二维面要素拉伸实现三维显示二、依据高度实现要素转3D一、二维面要素拉伸实现三维显示 打开ArcScene软件,加载实验配套数据0133.rar中的建筑物.shp数据,如下图: 数据属性表中的Z为建筑…

快速搭建SpringBoot3+Vue3+ElementPlus管理系统

快速搭建SpringBoot3Vue3管理系统 前端项目搭建(默认开发环境:node20,Jdk17)创建项目并下载依赖--执行以下命令 前端项目搭建(默认开发环境:node20,Jdk17) 创建项目并下载依赖–执行以下命令 创建项目 y…

力扣--LCR 134.Pow(x,n)

题目 实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。 示例 1: 输入:x 2.00000, n 10 输出:1024.00000 示例 2: 输入:x 2.10000, n 3 输出:9.2610…

道可云人工智能元宇宙每日资讯|全国工商联人工智能委员会成立会议在南京举办

道可云元宇宙每日简报(2024年12月5日)讯,今日元宇宙新鲜事有: 全国工商联人工智能委员会成立会议在南京举办 全国工商联人工智能委员会成立会议日前在江苏省南京市举办。中央统战部副部长、全国工商联党组书记沈莹出席会议并讲话…

基于Qt的文字处理软件(二)

这期文章我们进行主窗口的一些函数的定义,同时导入一些文字处理软件的状态栏会用到的图标。下面图片是图标导入到项目后的一个示例,图标可以到阿里矢量图标库里面找到。 一、导入图标资源: 1.首先在项目目录的位置创建一个images的文件,然后将收集好的图…

如何使用WinCC DataMonitor基于Web发布浏览Excel报表文档

本文介绍使用 WinCC DataMonitor 的 "Excel Workbooks" 功能,通过 Excel 表格显示 WinCC 项目的过程值、归档变量值和报警归档消息。并可以通过 Web 发布浏览访问数据 1.WinCC DataMonitor是什么 ? DataMonitor 是 SIMATIC WinCC 工厂智能中…

element plus table组件多选获取数据id

首先给table加上 selection-change"handleSelectionChange"事件 示例 <el-table selection-change"handleSelectionChange" stripe:data"data?.slice((currentPage3 - 1) * pageSize3, currentPage3 * pageSize3)" style"width: 100%…

信号的捕捉

目录 一、内核态与用户态的切换 二、 sigaction 参数介绍​编辑 三、 signal 四、某个信号在正在捕捉期间&#xff0c;该信号不允许再次被递达 五、可重入与不可重入函数&#xff08;描述函数的特性&#xff09; 六、volatile关键字 七、SIGCHLD信号 一、内核态与用户态的…