前端开发:JS中深拷贝和浅拷贝的区别

news/2024/5/19 8:46:46/文章来源:https://blog.csdn.net/CC1991_/article/details/129187409

前言

前端开发中,关于JS原生的原理使用是前端开发者的看家本领,尤其是关于底层和原理的掌握使用,甚为重要。而且编程语言有一些比较共性的概念在不同的编程语言中会有相同的概念,比如深拷贝和浅拷贝它们不仅在JS中有,在其他编程语言中也经常被提及到,而且在实际开发过程中也常常需要区分当前使用的到底是浅拷贝还是深拷贝,如果没有区分正确,在该需要使用深拷贝的时候使用了浅拷贝,那就容易埋下bug隐患,而且不易排查出来。在JS中深拷贝和浅拷贝的使用是比较常用的知识点,而且在前端求职面试的时候二者也是必考知识点,可以说非常重要,那么本文就来做一下总结,方便查阅使用。

JS数据类型

要谈浅拷贝和深拷贝之前,前提是要先聊聊JS的数据类型,众所周知,JS数据类型分为:基本数据类型和引用数据类型。基本数据类型包含number、string、boolean、Null、undefined和ES6语法的Symbol等,它们是存储在程序等栈内存中;引用数据类型包含Array、Object、function(函数)以及ES6语法的Set和Map等,它们是将其地址存储在程序的栈内存中,但真实数据存储在程序的堆内存中。另外,程序的内存又被分为栈内存和堆内存,不管是number、string、boolean、Null、undefined和ES6语法的Symbol还是Array、Object、function(函数)都会被存储在内存中。具体如图所示:

需要明确的一点:深拷贝和浅拷贝的概念只适用于Object(对象)或者Array(数组)等引用数据类型。

深拷贝与浅拷贝概念

浅拷贝:只拷贝数据在程序中的内存地址,而不是在程序内存中重新创建一个一模一样的Object(对象)或者Array(数组)。换句话说,浅拷贝是创建一个新对象,该对象有着原始对象属性值的一份完整的拷贝。若属性是基本类型,拷贝的就是基本类型的值;若属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝:会在程序内存中开辟一个新的存储空间,拷贝一个一模一样的Object(对象)或者Array(数组)。换句话说,深拷贝是将一个对象从内存中完整的拷贝一份出来,从程序的堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原来的对象。

深拷贝与浅拷贝的区别

浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。

深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。

浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级。

本质区别:是否真正获取一个对象的复制实体,而不是引用。深拷贝是新开辟一个新的地址空间,对象的改变不会影响原来的数组;浅拷贝只是复制原来的对象,指针仍然指向原来的数组,当前数组变化的时候会触发原来数组发生改变。

浅拷贝

根据上面浅拷贝的定义来讲,浅拷贝复制了对象(数组)存储在程序栈内存中的地址,而不是在程序内存中重新开辟一个新的存储空间用于存储新的对象,也可以说是两个对象共用一份内容。这里来举一个例子说明,浅拷贝示例如下所示:

let object_1 = {a:1,b:2}console.log(object_1); //输出结果为:{a:1,b:2}let object_2 = object_1; //具有代表性的浅拷贝操作console.log(object_2); //输出结果为:{a:1,b:2}object_1.a = 10; //修改原对象中的a属性值console.log(object_2); //由于公用同一份内容,所以属性a的值一起跟着改变,输出结果为:{a:10,b2}

 

上面的实例可得,如果object_1的内容发生改变时,object_2的内容也会跟着改变。原因就是:object_2拷贝了object_1在栈内存中的地址,也就是object_2和object_1共用存储在堆内存的数据;同理得,当object_2发生改变的时候,object_1也会随之改变。

深拷贝

同样根据上面的定义释义可知,深拷贝与浅拷贝最大不同是:浅拷贝只会拷贝栈内存中的数据地址,深拷贝会在内存中重新开辟一段新的存储空间,使得两个对象(数组)指向两个不同的堆内存数据,从而实现改变互不影响。这里来举一个例子说明,深拷贝示例如下所示:

let object_1 = {a:1,b:2}console.log(object_1); //输出结果为:{a:1,b:2}//在JS中使用JSON的stringify和parse就是深拷贝的一种方式。注意:该方法使用会有问题,实际开发中不建议使用,这里代码只做示例使用let object_2 = JSON.stringify(object_1); let object_3 = JSON.parse(object_2);console.log(object_3); //输出结果为:{a:1,b:2}object_1.a = 10; //修改原对象中的a属性值console.log(object_3); //由于object_3属于新创建的存储空间,所以不受object_1的影响,输出结果仍然为:{a:1,b:2} 

上面的实例可得,object_3是object_1的深拷贝对象,所以object_3不会随着object_1的改变而改变;同理得,当object_3发生改变的时候,object_1也不会随之改变。

深拷贝与浅拷贝的实现方式

浅拷贝的实现方式有

  1. Array.concat( );
  2. Array.slice( );
  3. Object.assign( ),它可以把任意多个源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

语法:Object.assign(target, source1, source2)

参数:target表示目标对象;source表示源对象

示例:

let object_1 = {a: 1,b: { f: { g: 1 } },c: [ 1, 2, 3 ]};let object_2 = Object.assign({}, object_1);console.log(object_2.b.f === object_1.b.f); //输出结果为:true

4、扩展运算符,利用扩展运算符可以在构造字面量对象时进行克隆或者属性拷贝。

语法:let cloneObject = { ...object };

示例:

var object = {a:1,b:{c:1}}var object2 = {...object};object.a=2;console.log(object); //输出结果为:{a:2,b:{c:1}}console.log(object2); //输出结果为:{a:1,b:{c:1}}object.b.c = 2;console.log(object); //输出结果为:{a:2,b:{c:2}}console.log(object2); //输出结果为:{a:1,b:{c:2}}

注意:扩展运算符与Object.assign()有同样的缺陷,那就是对于值是对象的属性无法完全拷贝成2个不同对象,但如果属性都是基本类型的值的时候,使用扩展运算符会更加方便。

深拷贝的实现方式有

  1. JSON.parse(JSON.stringify());
  2. jQuery.extend( );

语法:$.extend( [deep ], target, object1 [, objectN ] )

参数:deep为Boolean类型,指示是否深度合并对象,默认为false。若该值为true,且多个对象的某个同名属性也都是对象,则该"属性对象"的属性也将进行合并操作。

深拷贝与浅拷贝的应用场景

在JS中,不管是浅拷贝还是深拷贝,通常用于操作Object 或 Array等引用类型。如果实际开发中,只涉及一层结构的Array和Object拷贝一个副本使用的时候,使用浅拷贝;如果实际开发中,想对某个数组或对象的值进行修改,但又要保留原数组或原对象的值不被修改,这就需要使用深拷贝来创建一个新的数组或对象,进而达到在操作新的数组或对象时,保持原数组或对象不变。

拓展:递归实现完全拷贝

通过使用递归实现完全拷贝,具体示例代码如下所示:

function deepCopy(x) {var newX = Array.isArray(x) ? [] : {};for (key in x) {// 判断是否为对象if (typeof x[key] === 'object' && x[key]) {// 拷贝下面一层newX[key] = deepCopy(x[key]);} else {newX[key] = x[key];}}return newX;}let object = {a: 2, c: 3,d() { console.log(111111) },r: { a: 2 }}let object1 = deepCopy(object)console.log(object1.d === object1.d)

上面代码通过递归实现完全拷贝,而且没有舍弃函数,只是拷贝了函数的指针,符合实际业务开发需求,针对一个功能只写一个函数,可以反复调用即可。深拷贝只针对于引用数据类型,在使用过程中需要根据具体的使用场景选择不同的拷贝方式,若对象和数组中没有引用数据类型可以使用扩展运算符,更加方便。完全拷贝,一般不建议使用json的方法,因为会舍弃函数,可以直接封装一个函数使用递归来完全拷贝。

特别注意:函数只拷贝它的指针,可以根据符合相同功能一个函数的设计思想来操作。

最后

通过本文关于前端开发JS中深拷贝和浅拷贝的区别的详细介绍,深拷贝和浅拷贝不管是在实际的前端开发工作中还是在前端求职面试中都是非常关键的知识点,所以作为前端开发者来说必须要掌握它相关的内容,尤其是从事前端开发不久的开发者来说尤为重要,是一篇值得阅读的文章,重要性就不在赘述。欢迎关注,一起交流,共同进步。

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

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

相关文章

Prometheus之Alertmanager告警

告警流程 Prometheus主要是提供了数据的采集和存储,Alertmanager组件主要实现告警功能。Alertmanager 主要用于接收 Prometheus 发送的告警信息,它支持丰富的告警通知渠道,而且很容易做到告警信息进行去重,降噪,分组等…

双碳”目标下二氧化碳地质封存技术应用前景及模型构建实践方法

2022年七月七日,工业和信息化部、发展改革委、生态环境部关于印发工业领域碳达峰实施方案的通知落地。全国各省份积极响应,纷纷出台地方指导文件,标志着我国碳减排事业的全面铺开。二氧化碳地质封存技术作为实现我国“双碳”目标的重要一环&a…

易点易动助力企业固定资产信息化管理

对于生产制造或者互联网企业而言,固定资产比重较高,是企业资产的大头,一些办公设备、生产设备数量和金额都比较大。提升企业固定资产管理水平,是企业实现信息化建设的必要条件。 目前,国内的很多企业在固定资产管理中…

一口吃不成ChatGPT,复旦版MOSS服务器被挤崩后续

ChatGPT 是目前最先进的 AI,由于 ChatGPT 的训练过程所需算力资源大、标注成本高,此前国内暂未出现对大众开放的同类产品。 适逢ChatGPT概念正火,2 月 21 日,复旦团队发布首个中国版类 ChatGPT 模型「MOSS」,没想到瞬时…

编译原理【运行时环境】—什么是活动记录、 活动记录与汇编代码的关系

系列文章戳这里👇 什么是上下文无关文法、最左推导和最右推导如何判断二义文法及消除文法二义性何时需要消除左递归什么是句柄、什么是自上而下、自下而上分析什么是LL(1)、LR(0)、LR(1)文法、LR分析表LR(0)、SLR(1)、LR(1)、LALR(1)文法之间的关系编译原理第三章习…

扬帆优配|翻倍牛股“高台跳水”,一度跌停,啥情况

2月23日上午,A股商场窄幅震荡,上证指数上午收盘涨0.07%,煤炭、电力设备等板块领涨。 总的来看,A股商场上午整体体现安静,不过仍有个股大幅动摇,比如前期翻倍热门股汉王科技盘中“高台跳水”,一…

JS - 原型对象、原型链是什么

一 阅读掘金 https://juejin.cn/post/7007416743215759373 https://juejin.cn/post/7007416743215759373 二 阅读掘金小册原型知识点 原型 涉及面试题:如何理解原型?如何理解原型链? 当我们创建一个对象时 let obj { age: 25 }&#xff0…

一篇文章搞定linux网络模型

网络协议感觉晦涩难懂?什么七层网络模型?又五层网络模型?又四层网络模型?TCP/IP协议是个啥?UDP是啥?什么是三次握手?什么是四次挥手?tcpdump听说是抓包的,怎么用&#xf…

Docker 容器命令 和安装各种镜像环境

CentOS安装Docker 1.1.卸载(可选) 如果之前安装过旧版本的Docker,可以使用下面命令卸载: yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotat…

SCG failure information

我们知道5G网络有独立组网和非独立组网,独立组网中不论是核心网还是接入网都是5G,但是部署成本高;非独立组网也就是双连接(MRDC)也是目前比较流行的一种方式,其中的ENDC,即E-UTRA-NRDual Connectivity,是将…

设备太分散?如何一站式管理边缘 OS、K8s 和应用?

作者简介 张志龙,SUSE 大中华区资深解决方案架构师,CNCF 官方认证的 CKA&CKAD 工程师,深耕以 Kubernetes 为代表的云原生领域,具备丰富的架构设计、业务容器化改造和项目落地实践经验。 据 Gartner 预测,到 2025 年…

插画网课平台排名

插画网课平台哪个好,插画网课排名靠前的有哪些,今天给大家梳理了国内5家专业的插画网课平台,各有优势和特色,给学插画的小伙伴提供选择,报插画网课一定要选择靠谱的,否则人钱两空泪两行! 一&am…

node版本管理器 nvm

一、nvm介绍 在工作中,我们可能同时在进行2个或者多个不同的项目开发,每个项目的需求不同,进而不同项目必须依赖不同版本的NodeJS运行环境,这种情况下,对于维护多个版本的node将会是一件非常麻烦的事情,nv…

【2023】Prometheus-相关知识点(面试点)

目录1.Prometheus1.1.什么是Prometheus1.2.Prometheus的工作流程1.3.Prometheus的组件有哪些1.4.Prometheus有什么特点1.5.Metric的几种类型?分别是什么?1.6.Prometheus的优点和缺点1.7.Prometheus怎么采集数据1.8.Prometheus怎么获取采集对象1.9.Promet…

【测试面试】头条大厂,测试开发岗真实一面。你能抵得住吗?

目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 小吴: 现…

PHP7中Reids键空间通知配合TP5实现分布式延时任务的方法

本篇文章主要给大家介绍PHP7中Reids键空间通知配合TP5实现分布式延时任务的方法,希望对需要的朋友有所帮助! 测试环境:windows 10 phpStudy 配置redis配置文件 redis.windows.conf notify-keyspace-events "Ex" 重启redis服务 …

超实用的公众号运营攻略分享,纯干货

很多小伙伴抱怨,公众号运营真的越来越难做了! 每天会因为少得可怜的阅读量发愁,每天会因为纠结写什么选题发愁,每天更会因为公众号没有什么起色而感到无力。 现阶段公众号运营趋于饱和状态,公众号创建门槛低&#xf…

【unity细节】基于unity子对象(如相机)为什么无法进行z轴的拖拽移动和z轴自动归位的问题

👨‍💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏:unity细节和bug ⭐基于unity子对象为什么无法进行z轴的拖拽移动和z轴自动归位⭐ 文章目录⭐基于u…

百度西交大大数据菁英班目标检测竞赛

来源:投稿 作者:LSC 编辑:学姐 数据介绍 数据集共包括40000张训练图像和1000张测试图像,每张训练图像对应xml标注文件: 共包含3类:0:head, 1:helmet, 2:person。 提交格式要求,提交名为pred_r…

谷歌留痕代发技术指南_谷歌留痕怎么霸屏的?

本文主要分享谷歌留痕技术的一些常见问题,霸屏的原理是什么。 本文由光算创作,有可能会被修改和剽窃,我们佛系对待这种行为吧。 谷歌留痕也叫谷歌搜索留痕,那么谷歌搜索留痕的霸屏原理是什么? 答案是:利…