【手撕代码系列】JS手写实现深拷贝

news2024/10/7 14:29:41

在这里插入图片描述

公众号:Code程序人生,分享前端所见所闻

深拷贝是在计算机科学中非常重要的概念,尤其是在处理数据结构和对象的时候。深拷贝的目的是创建一个新的对象,它有自己的内存空间,并且其中的所有值都是原始对象的副本。这样做的好处是,可以避免在修改新对象时,对原始对象产生影响。

JavaScript 中,我们可以使用 JSON.parse()JSON.stringify() 方法来进行深拷贝,但是这种方法有一些限制,比如无法处理函数和循环引用等情况。因此,在这篇博客中,我们将手写实现深拷贝。

实现方法

首先,我们需要定义一个 deepClone 函数来进行深拷贝。这个函数接受一个对象作为参数,返回一个新的对象。在函数内部,我们需要对对象进行递归遍历,直到所有的值都被拷贝完成。
下面是一个简单的实现方法:

function deepClone(obj) {
  let clone = {};

  for (let key in obj) {
    if (typeof obj[key] === "object" && obj[key] !== null) {
      clone[key] = deepClone(obj[key]);
    } else {
      clone[key] = obj[key];
    }
  }

  return clone;
}

这个方法的实现比较简单,但是它无法处理函数和循环引用。接下来,我们将分别介绍如何处理这两种情况。

处理函数

要处理函数,我们需要在拷贝对象时,判断当前值是否为函数。如果是函数,我们需要使用Function.prototype.toString() 方法来获取函数的源代码,并使用 new Function() 来创建一个新的函数。

下面是一个实现方法:

function deepClone(obj) {
  let clone = {};

  for (let key in obj) {
    if (typeof obj[key] === "function") {
      clone[key] = new Function("return " + obj[key].toString())();
    } else if (typeof obj[key] === "object" && obj[key] !== null) {
      clone[key] = deepClone(obj[key]);
    } else {
      clone[key] = obj[key];
    }
  }

  return clone;
}

处理循环引用

循环引用是指一个对象中包含对自身的引用。处理循环引用是比较复杂的,需要使用一些技巧来判断是否已经拷贝过该对象。

一种实现方法是,使用一个 Map 对象来记录已经拷贝过的对象。每次遍历一个对象时,我们首先检查它是否已经被拷贝过。如果是,我们直接返回该对象的引用;如果不是,我们先将该对象添加到 Map 中,并递归地拷贝它的属性。

下面是一个实现方法:

function deepClone(obj, map = new Map()) {
  if (map.has(obj)) {
  	return map.get(obj);
  }
  
  let clone = {};
  
  for (let key in obj) {
  	if (typeof obj[key] === "function") {
  		clone[key] = new Function("return " + obj[key].toString())();
    } else if (typeof obj[key] === "object" && obj[key] !== null) {
      map.set(obj, clone);
      clone[key] = deepClone(obj[key], map);
    } else {
   	 	clone[key] = obj[key];
    }
	}
  
  return clone;
}

在这个方法中,我们添加了一个额外的参数 map,用来记录已经拷贝过的对象。如果该对象已经被拷贝过,我们直接返回该对象在 Map 中的引用。如果没有被拷贝过,我们将该对象添加到 Map 中,并递归地拷贝它的属性。

测试代码

为了测试我们实现的 deepClone 函数,我们可以创建一个包含不同类型的值的对象,并使用该函数进行拷贝。然后,我们可以修改拷贝后的对象,并检查原始对象是否发生了改变。 下面是一个测试代码示例:

下面是一个测试代码示例:

let originalObj = {
  a: 1,
  b: "hello",
  c: [1, 2, 3],
  d: { e: "world" },
  f: function () {
    console.log("hello world");
  },
};

let clonedObj = deepClone(originalObj);

clonedObj.a = 2;
clonedObj.b = "world";
clonedObj.c[0] = 4;
clonedObj.d.e = "world";

console.log(originalObj);
console.log(clonedObj);

输出结果如下:

{ a: 1, b: 'hello', c: [ 1, 2, 3 ], d: { e: 'world' }, f: [Function: f] }
{ a: 2, b: 'world', c: [ 4, 2, 3 ], d: { e: 'world' }, f: [Function: f] }

从输出结果可以看出,我们成功地拷贝了原始对象,并且在修改拷贝后的对象时,并没有影响到原始对象。

总结

在本篇博客中,我们介绍了如何手写实现深拷贝。我们实现了一个递归遍历对象的函数,同时处理了函数和循环引用的情况。这个方法虽然比较简单,但是它可以很好地处理大部分情况,避免在修改对象时对原始对象产生影响。

需要注意的是,手写实现深拷贝可能会带来一些性能上的问题。因为递归地遍历对象需要消耗大量的时间和内存。在处理大型对象或者嵌套层次很深的对象时,可能会出现性能问题。因此,建议在实际开发中,使用成熟的深拷贝库,如 lodashcloneDeep 方法,可以更好地处理性能问题。

另外,在手写实现深拷贝时,需要特别注意循环引用的问题。循环引用可能导致死循环,使程序崩溃。因此,我们需要使用一个 Map 来记录已经拷贝过的对象,以避免出现循环引用的问题。

总的来说,手写实现深拷贝是一个非常有用的技能。在实际开发中,我们经常需要处理对象的拷贝问题,特别是当我们需要对一个对象进行修改,但是又不想对原始对象造成影响时,深拷贝就显得非常有用了。

最后,值得一提的是,深拷贝并不是万能的解决方案。在某些情况下,浅拷贝甚至是更好的选择。因此,在实际开发中,我们需要根据具体情况选择合适的拷贝方式。

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

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

相关文章

某大型啤酒企业:CACTER邮件网关成功替换IronPort!安全防护升级

客户案例 某大型啤酒厂商的公司规模和市场份额多年来始终都处于行业领先地位,积极赞助多项体育赛事,持续丰富和提升品牌形象。 作为一家具有全球影响力的企业,自然也成为了全球黑客等攻击团伙的重点目标,而系统攻击的开端便是钓…

互联网医院牌照的申请流程|互联网医院资质申请难吗

互联网医院系统现在已经发展的很成熟了,国家也出台了很多鼓励的相关政策,所以很多的医疗机构也纷纷的开始开发互联网医院系统,当然要想互联网医院系统能够正常的运行看诊,还需要申办互联网医院牌照,接下来给大家介绍一…

GE Hydran M2 监测各种故障气体的综合值

Hydran M2 是一种紧凑型永久安装在线变压器监测设备,可在出现故障情况时向人员发出警报。它持续监测各种故障气体的综合值(以 ppm 为单位)或仅监测氢气值(取决于购买的传感器)。此外,它还跟踪油中的水分&am…

Redis自学之路—基础数据结构(二)

目录 简介 Redis应用场景 Redis基础数据结构 一、string(字符串) string类型相关指令 二、list(列表) list类型相关指令 三、hash(字典) hash类型相关指令 四、set(集合) se…

Mysql多表和窗口函数

1.多表查询--自关联查询 # 格式: select * from A join A on 条件; 或者 select * from A left join A on 条件; # 自关联查询的用法和 内连接, 外连接等操作一模一样, 只不过是: 表自己关联自己. # 应用场景: 分类表(多级), 行政区域表(3级, 省市区). # 查询结果: 跟上述多…

一起来学shiny把(2)—-shiny页面布局

什么是shiny?Shiny是一个R包,可让您轻松地直接从 R 构建交互式 Web 应用程序(应用程序)。本系列是个长教程,带你由浅入深学习shiny。 上一节我们在文章《R语言系列教程—–一起来学shiny吧(1)》…

什么是语音识别的语音搜索?

前言 随着智能手机、智能音箱等智能设备的普及,语音搜索已经成为了一种趋势。语音搜索不仅方便快捷,而且可以实现双手的解放。语音搜索的实现离不开语音识别技术,本文将详细介绍语音识别的语音搜索。 语音识别的基本原理 语音识别是将语音信…

高光谱图像处理的spectral模块一些用法

目录 1、安装 2、读取高光谱图像 3、显示高光谱图像 4、spectral的特点 5、标签图显示 6、标签、地物融合显示 8、显示三维立方体 9、保存图像 1、安装 pip install spectral -i https://pypi.tuna.tsinghua.edu.cn/simple 2、读取高光谱图像 # -*- coding:utf-8 _*…

react笔记_14在react中使用echarts

目录 echarts官网在项目引入echarts[1]下载[2-1] 全量引入[2-2]按需引入问题 - 仅引入核心模块 图表配置[1] 柱状图(bar)横/纵向柱状图 [2] 漏斗图(funnel)漏斗图的形状 echarts官网 echarts官网 在项目引入echarts [1]下载 npm install echarts [2-1] 全量引入 import *…

FPGA_学习_05_管脚约束

前言:就初学管脚约束相关知识而言,内容还不足以构成饱满的文章。 但管脚约束是一个独立的内容,它是值得有一篇单独的博客的。若后续学习了管脚约束新的知识,则进一步扩充本篇博客内容。 1 XDC基础语法 Vivado的管脚约束文件用XDC…

易基因:m5C高甲基化介导EGFR突变的非小细胞肺癌耐药潜在机理|国人佳作

大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。 对EGFR酪氨酸激酶抑制剂(EGFR-TKI)的固有耐药(Intrinsic resistance)和获得耐药(acquired resistance)是EGFR突变型非小细胞肺癌(NSCLC)患…

当上了小领导如何管理手下员工才能最高效的工作?

公司新建了一个项目,我负责招人并管理,之前没有正式的管理经验,如何才能更好的管理新人,快速推进工作? 在公司新建项目时,担任小领导的你需要负责招人和管理团队成员。作为一个没有正式管理经验的人&#x…

Elsevier Ocean Engineering Guide for Authors 解读

文章目录 ★Types of contributions★Submission checklistEthics in publishing★Declaration of competing interestDeclaration of generative AI in scientific writingSubmission declaration and verificationPreprint posting on SSRNUse of inclusive languageReportin…

vue-antd-admin加载动态菜单的步骤——vue3动态菜单——技能提升

最近在写后台管理系统时,发现老系统有个需求,就是动态加载菜单 以往的静态菜单:路由都放在router/config.js中,菜单页面放在pages里面。 加载的动态菜单:路由是通过接口获取,然后加载到路由中&#xff0c…

凌恩生物美文分享 | Nature教你如何深入开展植物基因组研究,看这篇绝对够!

自三代测序技术面世以来,基因组的相关研究迈上了一个新台阶,无论是完整性、连续性、准确性较二代测序技术组装基因组均有较大的提升。凌恩生物也紧随前沿,整合多种优势技术及信息分析平台,涵盖Illumina,Pacbio等多种测…

阿里主动改革,再次引领国内公司治理新浪潮

北京时间2023年5月18日美股盘前,阿里公布2023财年Q4及全年财报,整体财务状况符合市场预期,但推动组织变革的进度,却给了市场一个“惊喜”。 财报中,阿里宣布了整体组织变革背景下,旗下数个业务的未来方向&…

【HISI IC萌新虚拟项目】Package Process Unit模块整体方案·PART2

4.系统功能和主要技术指标论证 4.1芯片总体结构图 芯片总体结构框图如图2所示。 图 2 packet_process_unit 芯片结构框图 芯片内部主要电路包括 cpu_if 接口电路、sram 检测电路 test_core 以及 spt 包转发接口电 路。以下对主要部分分别进行简要介绍: 1.cpu_if 接口电路 该…

LOTO示波器如何测试阻抗的频响曲线

LOTO示波器如何测试阻抗的频响曲线 模块的输入输出端口,在电路分析上,一般简单表征为电阻来进行计算和分析。但多数情况下,这些端口并不是纯电阻的特性,更精确一些,它可能是电阻电容以及电感的组合,表现为非…

Vite打包优化

关于指标,这里简单介绍下常见的优化指标 FCP(First Contentful Paint):白屏时间(第一个文本绘制时间)Speed Index:首屏时间TTI(Time To Interactive): 第一次可交互的时l…

什么是合伙企业?普通合伙和有限合伙区别?

1.什么是合伙企业? 合伙企业是指由各合伙人订立合伙协议,共同出资,共同经营,共享收益,共担风险,并对企业债务承担无限连带责任的营利性组织。合伙企业一般无法人资格,不缴纳企业所得税,缴纳个…