javascript怎么实现数组深拷贝

数组深拷贝的核心是创建一个与原数组完全独立的新数组,修改新数组不会影响原数组。1. json序列化/反序列化:适用于仅含基本数据类型和普通对象且无循环引用的数组,优点是简单高效,缺点是无法处理函数、undefined、symbol及循环引用。2. 递归拷贝:可处理嵌套结构,需通过map记录已拷贝对象以避免循环引用导致的栈溢出,但仍无法直接复制函数和symbol。3. structuredclone:现代浏览器原生支持,性能较好且能处理date、regexp等特殊对象,但不兼容旧浏览器且无法复制函数和symbol。4. 浅拷贝后手动深拷贝:适用于仅需深拷贝部分元素的场景,可提升性能但需手动管理拷贝逻辑。5. lodash的_.clonedeep:功能最强大,支持复杂结构和循环引用,但需引入外部依赖且性能略低。选择方法应根据数组结构复杂度、是否含特殊类型、兼容性要求及性能需求综合判断。

javascript怎么实现数组深拷贝

数组深拷贝,说白了,就是创建一个新数组,这个新数组和原始数组完全独立,修改新数组不会影响到原始数组。

javascript怎么实现数组深拷贝

解决方案:

实现 JavaScript 数组深拷贝的方法有很多,各有优劣,选择哪个取决于你的具体需求和数组的复杂程度。

立即学习“Java免费学习笔记(深入)”;

javascript怎么实现数组深拷贝

JSON 序列化/反序列化:最简单粗暴,但有局限

这是最简单的方法之一,利用

JSON.stringify()

将数组转换为 JSON 字符串,再用

JSON.parse()

将字符串转换回数组。

const originalArray = [1, 2, { a: 3 }];const deepCopyArray = JSON.parse(JSON.stringify(originalArray));deepCopyArray[2].a = 4; // 修改深拷贝后的数组console.log(originalArray[2].a); // 输出 3,说明原始数组未被修改

优点:

javascript怎么实现数组深拷贝简单易懂,代码量少。

缺点:

无法拷贝函数、

undefined

Symbol

等特殊类型。如果数组中包含循环引用,会报错。性能相对较差,特别是对于大型数组。

所以,如果你的数组只包含基本数据类型和普通对象,并且没有循环引用,那么这种方法是最快的。

递归拷贝:更通用,但需要小心

递归拷贝是一种更通用的深拷贝方法,它可以处理更复杂的数据结构。

function deepCopy(obj) {  if (typeof obj !== 'object' || obj === null) {    return obj; // 如果不是对象或为 null,直接返回  }  const newObj = Array.isArray(obj) ? [] : {}; // 创建新对象或数组  for (let key in obj) {    if (obj.hasOwnProperty(key)) {      newObj[key] = deepCopy(obj[key]); // 递归拷贝    }  }  return newObj;}const originalArray = [1, 2, { a: 3, b: function() { console.log('hello'); } }];const deepCopyArray = deepCopy(originalArray);deepCopyArray[2].a = 4;console.log(originalArray[2].a); // 输出 3deepCopyArray[2].b(); // 报错,deepCopy无法复制函数

优点:

可以处理包含嵌套对象和数组的复杂数据结构。

缺点:

无法拷贝函数、

undefined

Symbol

等特殊类型(需要额外处理)。如果数组中包含循环引用,会导致无限递归,最终栈溢出。性能相对较差,特别是对于大型数组。

需要注意的是,递归拷贝需要处理循环引用的问题,否则会陷入无限循环。一种常见的处理方式是使用

Map

Set

来记录已经拷贝过的对象,避免重复拷贝。

使用

structuredClone

:现代浏览器原生支持

structuredClone

是一个现代浏览器提供的原生方法,用于深拷贝对象。

const originalArray = [1, 2, { a: 3, b: function() { console.log('hello'); }, c: undefined, d: Symbol('test') }];const deepCopyArray = structuredClone(originalArray);deepCopyArray[2].a = 4;console.log(originalArray[2].a); // 输出 3// deepCopyArray[2].b(); // 报错,structuredClone无法复制函数console.log(deepCopyArray[2].c); // undefined// console.log(deepCopyArray[2].d); // 报错,structuredClone无法复制Symbol

优点:

简单易用,一行代码搞定。性能相对较好,因为它是由浏览器原生实现的。可以处理

Date

RegExp

等特殊对象。

缺点:

兼容性问题,只在现代浏览器中支持。无法拷贝函数、

Symbol

等特殊类型。如果数组中包含循环引用,会报错。

structuredClone

在大多数情况下是一个不错的选择,但需要注意兼容性和无法拷贝的类型。

浅拷贝后手动深拷贝:针对特定场景的优化

有时候,你只需要深拷贝数组中的部分元素,而不是整个数组。这时,可以先进行浅拷贝,然后手动深拷贝需要深拷贝的元素。

const originalArray = [1, 2, { a: 3 }, 4];const shallowCopyArray = [...originalArray]; // 浅拷贝shallowCopyArray[2] = deepCopy(originalArray[2]); // 手动深拷贝对象shallowCopyArray[2].a = 4;console.log(originalArray[2].a); // 输出 3

优点:

可以针对特定场景进行优化,提高性能。

缺点:

需要手动处理,代码量相对较多。容易出错,需要仔细考虑哪些元素需要深拷贝。

这种方法适用于只需要深拷贝数组中少量元素的场景,例如,数组中大部分元素都是基本数据类型,只有少数元素是对象或数组。

lodash 的

_.cloneDeep

:功能强大,但引入依赖

lodash 是一个流行的 JavaScript 工具库,提供了很多实用的函数,包括深拷贝函数

_.cloneDeep

const _ = require('lodash');const originalArray = [1, 2, { a: 3, b: function() { console.log('hello'); } }];const deepCopyArray = _.cloneDeep(originalArray);deepCopyArray[2].a = 4;console.log(originalArray[2].a); // 输出 3// deepCopyArray[2].b(); // 报错,lodash默认无法复制函数

优点:

功能强大,可以处理各种复杂的数据结构。使用方便,一行代码搞定。

缺点:

需要引入 lodash 库,增加了项目的依赖。性能可能不如原生方法。

lodash 的

_.cloneDeep

是一个功能强大的深拷贝函数,可以处理各种复杂的数据结构,但需要引入 lodash 库。

哪种方法最适合你?

如果数组很简单,只包含基本数据类型和普通对象,并且没有循环引用,那么

JSON.stringify()

/

JSON.parse()

是最快的。如果需要处理包含嵌套对象和数组的复杂数据结构,并且没有循环引用,那么递归拷贝是一个不错的选择。如果你的目标浏览器支持

structuredClone

,并且不需要拷贝函数和 Symbol,那么

structuredClone

是最方便的。如果只需要深拷贝数组中的部分元素,那么浅拷贝后手动深拷贝可以提高性能。如果需要处理各种复杂的数据结构,并且不介意引入 lodash 库,那么

_.cloneDeep

是最强大的。

深拷贝时如何处理循环引用?

循环引用是指对象之间相互引用,例如:

const obj1 = { a: 1 };const obj2 = { b: obj1 };obj1.c = obj2; // obj1 引用 obj2,obj2 引用 obj1,形成循环引用

如果使用递归拷贝,遇到循环引用会导致无限递归,最终栈溢出。为了解决这个问题,可以使用

Map

Set

来记录已经拷贝过的对象,避免重复拷贝。

function deepCopy(obj, map = new Map()) {  if (typeof obj !== 'object' || obj === null) {    return obj;  }  if (map.has(obj)) {    return map.get(obj); // 如果已经拷贝过,直接返回  }  const newObj = Array.isArray(obj) ? [] : {};  map.set(obj, newObj); // 记录已经拷贝过的对象  for (let key in obj) {    if (obj.hasOwnProperty(key)) {      newObj[key] = deepCopy(obj[key], map);    }  }  return newObj;}const obj1 = { a: 1 };const obj2 = { b: obj1 };obj1.c = obj2;const deepCopyObj1 = deepCopy(obj1);deepCopyObj1.a = 2;console.log(obj1.a); // 输出 1

在这个例子中,

Map

用于记录已经拷贝过的对象。当递归拷贝遇到已经拷贝过的对象时,直接返回

Map

中记录的拷贝对象,避免重复拷贝,从而解决了循环引用的问题。

深拷贝函数和 Symbol 类型怎么办?

默认情况下,

JSON.stringify()

、递归拷贝和

structuredClone

都无法拷贝函数和 Symbol 类型。如果需要拷贝这些类型,需要自定义处理。

对于函数,一种简单的处理方式是直接返回原始函数,这意味着拷贝后的对象和原始对象共享同一个函数。

function deepCopy(obj, map = new Map()) {  if (typeof obj !== 'object' || obj === null) {    return obj;  }  if (typeof obj === 'function') {    return obj; // 直接返回原始函数  }  if (map.has(obj)) {    return map.get(obj);  }  const newObj = Array.isArray(obj) ? [] : {};  map.set(obj, newObj);  for (let key in obj) {    if (obj.hasOwnProperty(key)) {      newObj[key] = deepCopy(obj[key], map);    }  }  return newObj;}const originalObj = { a: 1, b: function() { console.log('hello'); } };const deepCopyObj = deepCopy(originalObj);deepCopyObj.b(); // 输出 hello,和原始对象共享同一个函数

对于 Symbol 类型,一种处理方式是忽略它,不进行拷贝。另一种处理方式是使用

Symbol.keyFor()

Symbol()

重新创建 Symbol。

function deepCopy(obj, map = new Map()) {  if (typeof obj !== 'object' || obj === null) {    return obj;  }  if (typeof obj === 'function') {    return obj;  }  if (typeof obj === 'symbol') {    const key = Symbol.keyFor(obj);    if (key) {      return Symbol.for(key); // 如果是全局 Symbol,重新创建    } else {      return Symbol(obj.description); // 如果是局部 Symbol,重新创建    }  }  if (map.has(obj)) {    return map.get(obj);  }  const newObj = Array.isArray(obj) ? [] : {};  map.set(obj, newObj);  for (let key in obj) {    if (obj.hasOwnProperty(key)) {      newObj[key] = deepCopy(obj[key], map);    }  }  return newObj;}const sym = Symbol('test');const originalObj = { a: 1, b: sym };const deepCopyObj = deepCopy(originalObj);console.log(deepCopyObj.b === sym); // 输出 false,重新创建了 Symbol

需要注意的是,拷贝函数和 Symbol 类型可能会带来一些问题,例如,拷贝后的函数可能无法访问原始对象的作用域,拷贝后的 Symbol 可能不再是唯一的。因此,需要根据具体情况选择合适的处理方式。

以上就是javascript怎么实现数组深拷贝的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1514167.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
如何利用事件循环实现高效的错误恢复?
上一篇 2025年12月20日 08:15:53
js怎么判断元素是否有某个class
下一篇 2025年12月20日 08:16:04

相关推荐

  • 搜狗输入法怎么换皮肤_搜狗输入法皮肤下载与更换

    换搜狗输入法皮肤需先找到入口,电脑端点击状态栏“衣服”或“S”图标进入皮肤盒子,预览后一键启用;2. 手机端在输入框调出键盘,点击“S”标志进入“皮肤”选项,下载即可自动应用;3. 支持手动安装.skin文件,可通过官网下载或本地导入,部分皮肤可自动更新样式。 想给搜狗输入法换个新皮肤,操作很简单,…

    2026年8月26日
    000
  • 发私信不能自动回复?私信设置自动回复

    在信息高速流通的今天,我们每天都会收到大量私信。为了提升沟通效率,不少个人用户和商家都启用了自动回复功能。然而,有时却发现发送私信后并未触发自动回复,令人困惑。究竟是什么原因导致这一现象?本文将深入剖析自动回复失效的四大核心原因。 一、网络连接异常 首要考虑的因素便是网络状况。若网络信号弱或连接不稳…

    2026年8月26日
    000
  • Swoft框架的依赖注入与AOP

    在swoft框架中,依赖注入和aop通过注解协同工作,提升代码的可维护性和可扩展性。1)依赖注入通过@inject注解实现组件解耦,提高代码的可测试性和灵活性。2)aop通过@aspect和@around注解实现横切关注点的分离,如日志记录,增强代码的模块化和可重用性。 在Swoft框架中,依赖注入…

    2026年8月26日
    600
  • 告别PHP命令行参数混乱:nategood/commando助你打造优雅CLI工具!

    可以通过一下地址学习composer:学习地址 命令行工具开发的“痛点” 作为php开发者,我们经常需要编写一些命令行脚本来执行自动化任务、数据处理或系统维护。一开始,我们可能习惯于直接使用php内置的 $argv 超全局变量来获取命令行参数,或者尝试使用 getopt() 函数进行稍微结构化的解析…

    用户投稿 2026年8月26日
    000
  • 摄像机怎么恢复出厂设置_摄像机恢复出厂设置的操作步骤与注意事项

    恢复出厂设置可解决登录问题或清除配置,适用于转售设备。首先通过物理按键复位,适用于带Reset孔的摄像机,通电状态下长按5-10秒至指示灯红蓝闪烁即可;其次可通过Web界面操作,需登录同网段摄像机IP,在系统维护中点击恢复出厂设置;第三种方式为手机APP远程重置,解绑设备后重新添加并勾选强制恢复选项…

    2026年8月26日
    200
  • 抖音平台优惠券会给卖家补贴吗?抖音平台优惠券会给卖家补贴吗是真的吗

    随着移动互联网的迅猛发展,抖音已成长为国内最具影响力的短视频社交平台之一。目前,抖音的日活跃用户已突破5亿大关,海量用户每天在平台上浏览、分享和创作短视频内容。与此同时,平台的营销手段也日益丰富,其中优惠券活动成为商家常用的推广方式之一。那么,抖音平台推出的优惠券活动是否真的会为卖家提供补贴?本文将…

    2026年8月26日
    000
  • 如何解决HEIC/AVIF图片转换难题?使用Composer和heif-converter轻松搞定!

    可以通过一下地址学习composer:学习地址 告别 HEIC/AVIF 图片兼容性烦恼:用 Composer 玩转 heif-converter 相信很多朋友都有过这样的经历:朋友用 iphone 拍了张照片发给你,结果你发现它是个 .heic 文件。或者,你在网上下载了一些高质量的图片,发现它们…

    用户投稿 2026年8月26日
    000
  • 电脑找不到vcruntime140_1.dll无法继续执行代码的解决方法

    电脑找不到vcruntime140_1.dll无法继续执行代码的解决方法电脑找不到vcruntime140_1.dll无法继续执行代码的解决方法电脑找不到vcruntime140_1.dll无法继续执行代码的解决方法电脑找不到vcruntime140_1.dll无法继续执行代码的解决方法

    打开某个软件或游戏时,突然提示“找不到vcruntime140_1.dll,无法继续执行代码”?这类错误通常源于系统缺少必要的运行库支持。接下来,我们一起来看看几种实用的解决方案。 一、为什么会提示“vcruntime140_1.dll缺失”? vcruntime140_1.dll 是微软 Visu…

    2026年8月26日 用户投稿
    000
  • 如何在ThinkPHP中使用事务(Transaction)?

    在thinkphp中使用事务的步骤是:1. 使用db::starttrans()启动事务;2. 在try块中执行数据库操作;3. 成功时使用db::commit()提交事务,失败时在catch块中使用db::rollback()回滚事务。使用事务时需注意事务范围、异常处理、并发控制和性能优化,避免死…

    2026年8月26日
    100
  • Java中如何旋转图片 分析图像旋转的实现

    Java中如何旋转图片 分析图像旋转的实现Java中如何旋转图片 分析图像旋转的实现Java中如何旋转图片 分析图像旋转的实现Java中如何旋转图片 分析图像旋转的实现

    图像旋转通过坐标变换实现,核心步骤包括确定旋转中心、计算旋转矩阵、应用变换、处理边界及插值。旋转中心通常为图像中心,也可自定义;旋转矩阵描述二维空间中绕点逆时针旋转的数学关系;使用逆矩阵将目标像素映射回原始坐标;旋转后图像可能超出边界,需裁剪或填充;插值常用最近邻、双线性或双三次方法,其中双线性在速…

    2026年8月26日 用户投稿
    300
  • win8怎么打开注册表编辑器_win8注册表编辑器打开方法

    1、通过Win+R输入regedit可快速打开注册表编辑器;2、开始屏幕搜索“注册表编辑器”能直接启动应用;3、任务管理器中运行新任务并启用管理员权限可高权限打开;4、文件资源管理器地址栏输入C:Windowsregedit.exe可执行程序;5、命令提示符或PowerShell输入regedit命…

    2026年8月26日
    000
  • AI做文本校对怎么用_GrammarlyAI语法检查高级技巧

    AI校对是效率工具但不能替代人工,正确用法是将其作为助手。首先用Grammarly检查拼写、语法,再利用其风格一致性、上下文分析功能优化表达;结合自定义规则和主动学习提升匹配度。使用时需结合语境判断建议合理性,重点修改不确定内容,并积累常见错误经验。隐私方面要注意数据上传风险,敏感内容应选本地或开源…

    2026年8月26日
    000
  • 私信自动回复卡片违规吗?私信自动回复卡片违规吗安全吗

    随着社交媒体的普及,越来越多的人开始使用私信功能进行交流。近年来,一种名为“私信自动回复卡片”的现象在社交媒体上引起了广泛争议。这种自动回复卡片真的违规吗?本文将为您揭开这一谜团。 一、什么是私信自动回复卡片? 在社交媒体上,许多用户为了方便快捷地回复私信,会选择使用自动回复卡片。这种卡片通常包含一…

    2026年8月26日
    000
  • 游戏服务器(Game Server)的Swoole实践

    选择swoole构建游戏服务器的原因是其高性能的异步网络通信引擎和事件驱动模型,适合处理大量实时连接和数据交互。swoole能提高响应速度、管理资源、减少延迟,提升玩家体验;其异步特性和协程功能有效应对高并发场景,代码编写高效直观。 提到游戏服务器(Game Server),在使用Swoole进行实…

    2026年8月26日
    100
  • 如何优雅地管理PHP异步操作?GuzzlePromises与Composer助你告别回调地狱

    可以通过一下地址学习composer:学习地址 告别“回调地狱”:PHP异步编程的优雅之道 想象一下,你正在开发一个复杂的web服务,需要同时从多个外部api获取数据,或者执行一系列耗时的数据处理任务。如果你按照传统的同步方式编写代码,用户就得眼睁睁地看着页面转圈,直到所有操作完成。这显然不是一个好…

    用户投稿 2026年8月26日
    100
  • 键盘一直自动按一个键怎么解决 巧用这招

    键盘一直自动按一个键怎么解决 巧用这招键盘一直自动按一个键怎么解决 巧用这招键盘一直自动按一个键怎么解决 巧用这招键盘一直自动按一个键怎么解决 巧用这招

    一、排查键盘是否存在物理故障 1. 检查按键是否被异物卡住:长时间使用后,键盘缝隙容易积聚灰尘、碎屑或食物残渣,可能导致某个按键无法正常回弹,从而持续触发。可以将键盘翻转并轻轻拍打背面,或使用压缩空气吹扫键帽周围。 2. 观察按键结构是否损坏:频繁敲击会使某些常用键(如空格、回车、字母E等)出现磨损…

    2026年8月26日 用户投稿
    100
  • Windows10更新失败提示“你的设备中缺少重要的安全和质量修复”

    最近有些用户在升级win10系统时,遇到了更新失败的问题,系统提示缺少关键的安全与质量补丁,该如何应对呢?其实,在更新windows系统时,建议您暂时关闭第三方的安全防护软件,并且拔掉除了鼠标和键盘之外的所有外接设备,然后再尝试更新,这样就能迅速解决问题了! 当前有两个办法可以供您参考,尝试解决这个…

    2026年8月26日
    100
  • Java中如何填充颜色 掌握区域填充的实现

    Java中如何填充颜色 掌握区域填充的实现Java中如何填充颜色 掌握区域填充的实现Java中如何填充颜色 掌握区域填充的实现Java中如何填充颜色 掌握区域填充的实现

    在java中填充颜色,核心在于操作图像像素并使用java的图像处理api。1. 创建bufferedimage对象作为图像缓冲区;2. 通过creategraphics()获取graphics2d对象用于绘制;3. 使用setcolor()设置填充颜色;4. 调用fillrect()或fill()方…

    2026年8月26日 用户投稿
    000
  • 聊聊zfs中的write

    以下是关于zfs和zpool的伪原创内容,保持了原文的结构和大意,同时进行了改写: // 创建一个zpool$ modprobe zfs$ zpool create -f -m /sample sample -o ashift=12 /dev/sdc$ zfs create sample/fs1 -…

    2026年8月26日
    000
  • ThinkPHP的目录结构及各文件作用解析

    thinkphp的目录结构包括根目录下的application/、public/、thinkphp/、vendor/、extend/、runtime/和config/,以及application/下的controller/、model/、view/和config/。关键文件如route.php、da…

    2026年8月26日
    000

发表回复

登录后才能评论
关注微信