什么是拷贝?我:Ctrl + C ...

前言

当谈及拷贝,你的第一印象会不会和我一样,ctrl c + ctrl v ... ;虽然效果和拷贝是一样的,但是你知道拷贝的原理以及它的实现方法吗?今天就让我们一起探究一下拷贝中深藏的知识点吧。 

拷贝

首先来看下面一段代码;

let obj = { age: 18 } let obj2 = obj obj.age = 20 console.log(obj2.age);

上面的let obj2 = obj这一操作能叫拷贝吗?并不能;因为这个操作只是让obj2 只是获得了 obj引用地址,这意味着 obj2obj 指向内存中的同一个对象obj2并未创建一个和obj一样的新对象;当对象obj身上的age改变时,因为二者的引用地址一样obj2.age也会跟着变化。

image.png

所以,在JavaScript中,"拷贝"(Copy)是指创建一个对象或数组(引用类型)的副本。根据拷贝的层次,可以分为两种类型:浅拷贝(Shallow Copy)和深拷贝(Deep Copy)。接下来我们就来细聊它们的区别以及实现的方式:

image.png

浅拷贝

浅拷贝(Shallow Copy),它创建一个新对象作为原对象的副本;但是在创建的新对象上,只有原始类型(如Number,String,Boolean等)的值会和原对象上的保持相同,而新对象上的引用类型会随着原对象的改变而改变,因为浅拷贝并不会递归地复制这些引用类型内部的元素,而是将引用的地址复制过去,这就导致新对象和原对象虽然在顶层看似独立,但是共享了内部引用类型的数据

image.png

所以说,浅拷贝后的新对象会受到原对象的影响;那在js中有哪些方法可以实现浅拷贝呢?

1. Object.create(obj)

在JavaScript 中,Object.create() 用于创建一个新对象,同时指定新创建的对象的原型(即其内部的 [[Prototype]] 隐式原型属性)。

 

ini

复制代码

let obj = { a : 1 } let newObj = Object.create(obj) console.log(newObj); console.log(newObj.a);

在上面的例子中,使用Object.create(obj)创建了一个新对象newObj,并把它的原型(prototype)设置为obj;这意味着新创建的对象newObj会继承obj的中的a,使用newObj.a同样能访问到其原型上的a的值。

image.png

但使用 Object.create(obj) 的方式不算是传统的“拷贝”,这个方法主要是用来建立对象之间的原型链关系实现原型继承,使得新对象可以访问到obj上的属性和方法。

2. Object.assign({},obj)

在JavaScript中,assign() 方法是 Object 构造函数的一个静态方法,用于将一个或多个源对象的可枚举属性的值复制到目标对象中。此方法会在目标对象上进行就地修改,并返回修改后的目标对象。

 

css

复制代码

let obj = { a: 1, b: [1,2] } let obj2 = Object.assign({},obj) // Object.assign(a,b) 将对象b合并到a中 obj.b.push(3) console.log(obj2.b);

image.png

利用这一特性,我们可以通过Object.assign({},obj)来完成浅拷贝的操作;将目标对象里的属性都复制到一个空对象中,但请注意,如果 obj 中的属性是引用类型(如数组、对象),只会复制引用类型在堆中的地址,这些属性在新对象中仍然是通过引用共享的,且会被原对象影响。

3. [].concat(arr)

[].concat(arr) 是 JavaScript 中用于将数组 arr 连接到一个新数组中的方法。它会返回一个新数组,其中包含原始数组和 arr 中的所有元素。利用这个方法,我们可以完成一个数组的拷贝

 

ini

复制代码

let arr = [1,2,3,{a:1}] let arr2 = [].concat(arr) arr[3].a = 2 console.log(arr2);

image.png

同样的,当要拷贝的数组中存有引用类型的值(如对象)时,拷贝过程中也只能复制其引用地址引用类型在新数组中同样也是共享原数组的

4. 数组解构 ...

在数组中,也可以通过解构的方法,实现浅拷贝;在js中解构数组用...表示

 

ini

复制代码

let arr = [1,2,3,{a:1}] let arr2 = [...arr] // 将原数组元素解构到新的空数组当中 arr[3].a = 2 console.log(arr2);

image.png

与使用 concat 方法一样,使用...扩展运算符进行浅拷贝也会导致新数组中的对象或数组元素仍然是原始数组中相同对象或数组的引用

5. arr.slice(0)

在js中,数组身上的slice方法,是用于从数组中提取一个新的子数组的方法。它接受两个参数,起始索引和结束索引(可选),并返回一个包含从起始索引到结束索引(不包括结束索引)的元素的新数组。如果省略结束索引,则提取从起始索引到数组末尾的所有元素。利用这一方法,也可以实现数组的浅拷贝:

 

ini

复制代码

let arr = [1,2,3,{a:1}] let arr2 = arr.slice(0) arr[3].a = 2 console.log(arr2);

image.png

6. arr.toReversed().reverse()

在js中,我们还可以利用“两极反转”的特性实现数组浅拷贝的功能。数组中,有一个方法叫做,toReversed(),它可以把原数组给反转,但是它并没有返回值;而数组中还有一个方法叫做,reverse(),它的作用和toReversed()是一样的,也是反转数组,但是它会返回反转后的数组。利用这个特点,我们就可以将这两种方法结合,两极反转一下,实现浅拷贝:

 

ini

复制代码

let arr = [1,2,3,{a:1}] let arr2 = arr.toReversed().reverse() arr[3].a = 2 console.log(arr2);

image.png

手写方法实现浅拷贝

浅拷贝的原理,即是创建一个副本对象,并把原对象的原始类型值复制过来,而引用类型则是复制其引用地址。在知道原理后,手动写一个实现浅拷贝的方法也很简单:

 

vbnet

复制代码

function shallowCopy(obj){ let newObj = {} for(let key in obj){ // key 是不是obj显示具有的 if (obj.hasOwnProperty(key)){ newObj[key] = obj[key] } } return newObj; } let obj = { a: 1, b: {n:2} } console.log(shallowCopy(obj));

image.png

在上面所示的方法中,是利用 for...in 循环拿到要拷贝对象中的属性;由于,for...in 循环在JavaScript 中,可以遍历对象的可枚举属性,包括自身的属性和继承的属性。这意味着在遍历过程中,可能会访问到对象的隐式属性,例如原型链上的属性。通常情况下,我们是不需要拷贝对象原型上的属性的,所以我们可以结合 hasOwnProperty 方法进行判断,规避原对象隐式具有的属性,以确保只遍历对象自身的属性

深拷贝

知道了浅拷贝之后,深拷贝也很好理解;深拷贝指的是将一个对象完整地复制到一个新的对象中,包括其所有属性和嵌套对象的属性,而不是仅仅复制其引用。这样做可以确保新对象与原始对象完全独立,修改新对象不会影响原始对象。

image.png

深拷贝的对象是不会受原对象的影响的。在js中有以下方法可以实现深拷贝:

1. JSON.parse(JSON.stringify(obj))

在js中,JSON.parse(JSON.stringify(obj)) 是一种常用的深拷贝对象的方法。它利用了 JSON 对象的序列化和反序列化功能,通过将对象转换为 JSON 字符串,再将其解析为新的对象,从而实现深拷贝。

 

ini

复制代码

let obj = { a: 1, b: {c: 2 } }; let newObj = JSON.parse(JSON.stringify(obj)); newObj.a = 3; newObj.b.c = 4; console.log(obj.a); // 输出 1 console.log(obj.b.c); // 输出 2

image.png

可以看到,当修改 newObj 的属性时,原始对象 obj 的属性并没有受到影响,这表明深拷贝成功实现了对象的完全独立复制。

但是JSON.parse(JSON.stringify(obj)) 方法存在一些局限性:

  1. 无法识别bigInt类型
  2. 无法拷贝 undefined,function,Symbol
  3. 无法处理循环引用

在大多数情况下,JSON.parse(JSON.stringify(obj)) 是一个简单且有效的深拷贝方法,但在处理特殊类型的属性或循环引用时,可能需要考虑其他深拷贝的实现方式。

2. structuredClone()

structuredClone() 是一个较新的JavaScript API,它提供了一种创建对象、数组以及一些特殊类型值的深拷贝的方法

 

yaml

复制代码

let obj = { a: 1, b: {n: 2}, c: 'cc', d: true, e: undefined, f: null, // g: function(){}, // h: Symbol(1), i: 123n } const newObj = structuredClone(obj) obj.b.n = 20 console.log(newObj);

image.png

在上面的执行结果中,可以看到structuredClone() 方法,不仅能实现深拷贝,而且还能够识别bigInt类型的值,以及undefined;并且structuredClone() API 有一个显著的优点,即它能够妥善处理对象的循环引用

手写方法实现深拷贝

在我们要手写一个深拷贝的方法时,你脑子里会用什么思想去实现它?没错,就是递归;因为深拷贝就是要把对象中的引用类型(对象等)里面的原始值给复制过来,遇到引用类型,就要获取里面的原始值,直至没有引用类型了

 

ini

复制代码

let obj = { a: 1, b: {n: 2} } function deepCopy(obj){ let newObj = {}; for(let key in obj){ if(obj.hasOwnProperty(key)){ // obj[key] 是不是对象 typeof(obj[key]) == 'object' && if (obj[key] instanceof Object){ newObj[key]=deepCopy(obj[key]) }else { newObj[key] = obj[key]; } } } return newObj; } let obj2 = deepCopy(obj) obj.b.n = 20 console.log(obj2);

image.png

在上面的代码中,obj[key] instanceof Object这段代码用于检查 obj 对象中键为 key 的属性值是否为对象,这意味着它会判断该属性值是否是一个对象(普通对象、数组、函数等,但不包括null,因为null没有原型);如果是对象类型的话,则递归调用;直到obj[key] 不是一个对象类型时,就会停止递归调用。

总结

浅拷贝适用于对象结构简单且不需要复制嵌套对象的情况,而深拷贝则适用于对象结构复杂,需要完全独立复制所有层级属性的场景。选择哪种拷贝方式取决于具体需求,但需注意深拷贝因为需要递归处理,所以在性能上相对较低效

 

仅供参考!!!

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

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

相关文章

Maya 白膜渲染简单教程

零基础渲染小白,没关系,一篇超简单教程带你学会渲染白膜。 先打开Maya,看看面板有没有渲染器,这里以Arnold为主。 要是没有这个,就去找插件管理器, Arnold的是mtoa,在搜索栏搜,然后把…

能ping通,但无法上网,看网络高手是如何解决?

经常会发现有一种现象,就是ip能ping通,但是就是无法上网,基本上每位行业的人员都遇到过。 出现这个问题原因主要有以下几个,我们一起来看下。 原因一、ip冲突 所谓的ip冲突,就是由于网络中出现了两个同样的ip地址&a…

React常用方法汇总【更新中】

文章目录 前言创建项目启动命令列表渲染useEffect 异步函数使用方法useEffect 异步函数清除方法控制组件显示隐藏axios 安装使用 前言 运行 react 需要先安装 node.js,具体安装步骤可以参考这篇文章 https://blog.csdn.net/weixin_43721000/article/details/134284…

掌握这三点软文营销技巧:轻松助力品牌传播

在营销方式层出不穷的今天,软文推广已不再只是简单的文字堆砌,而是成为了品牌与消费者深度沟通的桥梁。随着消费者获取信息的渠道越来越多元化,软文推广也迎来了新的趋势和挑战。今日投媒网将与您分享如何在新时代背景下,提升软文…

人工智能技术应用笔记(十二):搭建自带大模型微信,完美对接GPT-4o,Kimi等大模型,智能体平台Coze也能接

许多朋友对如何搭建自己的微信机器人非常感兴趣。今天就来教大家如何操作。 一、 准备工作 一台电脑或者云服务器,对配置要求不高,一般的电脑就行 大模型API调用的Key,比如GPT-4o,Kimi,Deepseek(不知道怎…

个人博客测试用例设计

个人博客测试用例设计 个人博客测试用例 分别从功能、性能、安全、兼容及界面分别展开 个人博客测试用例

工业边缘计算网关在机械制造企业中的应用-天拓四方

随着信息技术的不断发展,工业领域对数据处理和分析的需求日益增长。工业边缘计算网关作为一种新型技术,正逐渐成为工业数字化转型的关键驱动力。本文将通过一个具体案例阐述工业边缘计算网关在工业中的应用,以及其为工业生产带来的显著效益。…

远程桌面有时连接不上,远程桌面时连接不上的七个处理方法

远程桌面连接作为一种方便、高效的远程管理工具,广泛应用于企业办公、技术支持以及在线教育等多个领域。然而,在实际应用中,用户有时会遇到远程桌面连接不上的情况,这不仅影响了工作效率,还可能导致重要任务的延误。因…

Linux系统:线程互斥

Linux系统:线程互斥 线程互斥互斥锁 mutex互斥锁原理 常见的锁死锁自旋锁 spinlock其它锁 线程互斥 讲解线程互斥前,先看到一个抢票案例: class customer { public:int _ticket_num 0;pthread_t _tid;string _name; };int g_ticket 10000…

vue element-ui 车牌号选择组件

实现效果 carnumber.vue <template><div class"car_no_popover"><div class"row_div"><div class"every_div" v-for"item in area"><button click"selectCarNo(item)">{{ item }}</butto…

【GlobalMapper精品教程】086:批量修改符号的样式

跟ArcGIS一样,Globalmapper也可以批量修改符号的样式,如样式、填充颜色、轮廓等等。 文章目录 一、加载实验数据二、土地利用符号化三、批量修改符号样式四、注意事项一、加载实验数据 订阅专栏后,从私信查收专栏配套的完整实验数据包,打开data086.rar中的土地利用数据,如…

基于PaddleDetection的电路板瑕疵检测

文章目录 1. 数据集与框架介绍2. 任务详情3. Cascade R-CNN简介4. 数据分析各类别样本的数量真实框的宽高比真实框在原图的大小比例 5. 相关配置数据增强数据集路径和评估指标学习率和优化器配置预训练CascadeRCNN 的配置日志记录 6. 训练预测7. 参考链接 1. 数据集与框架介绍 …

SpringMVC系列二: 请求方式介绍

RequestMapping &#x1f49e;基本使用&#x1f49e;RequestMapping注解其它使用方式可以修饰类和方法可以指定请求方式可以指定params和headers支持简单表达式支持Ant 风格资源地址配合PathVariable 映射 URL 绑定的占位符注意事项和使用细节课后作业 上一讲, 我们学习的是Spr…

2024年618购物狂欢节买什么最超值!618不可错过的好物清单!

618年中大促再度来临。对于购物爱好者来说&#xff0c;这无疑是一个不容错过的购物盛宴。那么&#xff0c;在这个618&#xff0c;哪些好物值得你重点关注呢&#xff1f;我特地整理了一份推荐清单&#xff0c;这些产品不仅经过我的亲身体验&#xff0c;更以其出色的实用性和高性…

【嵌入式】适配大多数STM32的最小系统设计

大多数的参赛作品以及毕业设计中都仅仅简单的使用STM32F103最小系统板加上洞洞板或面包板。这样的设计虽然可以进行功能演示&#xff0c;但是由于市面上的最小系统板太过简单&#xff0c;并没有发挥出STM32系列MCU的强大功能&#xff0c;而且看起来很不美观&#xff0c;为了在参…

Scott Brinker:API对今天的Martech用户来说「非常重要」 ,但它们对即将到来的人工智能代理浪潮至关重要

API在Martech中非常重要 猜一猜空格应该填什么&#xff1a; _______之于AI代理就像数据之于AI模型 正如你可能从我的标题猜到的那样&#xff0c;答案是API。让我们讨论一下为什么…… 数据是人工智能模型的差异化 在过去的一年半里&#xff0c;人工智能疯狂的超级炒作周期…

阻力支撑相对强度(RSRS)选股系列报告之三

https://download.csdn.net/download/SuiZuoZhuLiu/89447699?spm1001.2014.3001.5503https://download.csdn.net/download/SuiZuoZhuLiu/89447699?spm1001.2014.3001.5503

高薪项目经理都在用的工具,你会几个?

甘特图、里程碑图、看版图、燃尽图、WBS、思维导图、流程图、鱼骨图、清单表、项目日历、关系矩阵、PERT图、决策树、状态表。这些工具&#xff0c;你会几个&#xff1f; 甘特图&#xff08;Gantt Chart&#xff09; 甘特图是一种条形图&#xff0c;用于展示项目任务的时间线&…

所以spring mvc异常处理工作原理是啥

文章目录 spring mvc异常处理&#xff08;源码分析&#xff09;概述原理&#xff08;源码角度&#xff09;模拟debug前期提要分析4个map4个map的初始化为什么需要基于mappedMethods缓存 总结一下 spring mvc异常处理&#xff08;源码分析&#xff09; 概述 spring mvc有下面三…

压测怎么做?如何自动化?盘点各大公司全链路压测方案与实践

本文综合盘点各大公司团队的全链路压测技术方案和实践路径&#xff0c;供大家参考。 一、什么是全链路压测&#xff1f; 全链路压测指的是基于实际的生产业务场景、系统环境&#xff0c;模拟海量的用户请求和数据对整个业务链进行压力测试&#xff0c;并持续调优的过程。常用…