什么是拷贝?我: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] 不是一个对象类型时,就会停止递归调用。

总结

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

 

仅供参考!!!

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

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

相关文章

MySQL数据库回顾(1)

数据库相关概念 关系型数据库 概念: 建立在关系模型基础上,由多张相互连接的二维表组成的数据库。 特点: 1.使用表存储数据,格式统一,便于维护 2.使用SQL语言操作,标准统一,使用方便 SOL SQL通用语法 …

粒子群算法PSO优化BP神经网络预测MATLAB代码实现(PSO-BP预测)

本文以MATLAB自带的脂肪数据集为例,数据集为EXCEL格式,接下来介绍粒子群算法优化BP神经网络预测的MATLAB代码步骤,主要流程包括1. 读取数据 2.划分训练集和测试集 3.归一化 4.确定BP神经网络的隐含层最优节点数量 5. 使用粒子群算法优化BP的神…

强得离谱,AI音乐的 Stable Diffusion: MusicGen

节前,我们星球组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学。 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 合集&#x…

打造完美Mac多屏视界,BetterDisplay Pro一键掌控!

BetterDisplay Pro for Mac是一款专为Mac用户打造的显示器管理与优化软件,旨在为用户带来卓越的视觉体验和工作效率。它凭借强大的功能和简洁易用的界面,成为了Mac用户优化显示器设置的得力助手。 一、全方位管理与优化 BetterDisplay Pro for Mac支持…

探索Python的多媒体解决方案:ffmpy库

文章目录 探索Python的多媒体解决方案:ffmpy库一、背景:数字化时代的多媒体处理二、ffmpy:Python与ffmpeg的桥梁三、安装ffmpy:轻松几步四、ffmpy的五项基本功能1. 转换视频格式2. 调整视频质量3. 音频转换4. 视频截图5. 视频合并…

java架构设计-COLA

参考:https://github.com/alibaba/COLA 架构 要素:组成架构的重要元素 结构:要素直接的关系 意义:定义良好的结构,治理应用复杂度,降低系统熵值,改善混乱状态 创建COLA应用: mvn …

Git的3个主要区域

一般来说,日常使用只要记住下图6个命令,就可以了。但是熟练使用,恐怕要记住60~100个命令。 下面是我整理的常用 Git 命令清单。几个专用名词的译名如下。 Workspace:工作区 Index / Stage:暂存区 Reposito…

你的企业真的适合做私域吗?

现在,都在提倡企业做私域,可是所有的企业都适合做私域吗?看看市场上成功的案例,显然,并不是所有企业都适合做私域,所以,做私域之前,企业也应该充分的分析,自己的优势是什…

spark常见问题

写文章只是为了学习总结或者工作内容备忘,不保证及时性和准确性,看到的权当个参考哈! 1. 执行Broadcast大表时,等待超时异常(awaitResult) 现象:org.apache.spark.SparkException: Exception…

玩转OurBMC第八期:OpenBMC webui之通信交互

栏目介绍:“玩转OurBMC”是OurBMC社区开创的知识分享类栏目,主要聚焦于社区和BMC全栈技术相关基础知识的分享,全方位涵盖了从理论原理到实践操作的知识传递。OurBMC社区将通过“玩转OurBMC”栏目,帮助开发者们深入了解到社区文化、…

【网络】序列化和反序列化

一、序列化和反序列化 序列化和反序列化是计算机中用于数据存储和传输的重要概念。 1.序列化 (Serialization) 是将数据结构或对象转换成一种可存储或可传输格式的过程。在序列化后,数据可以被写入文件、发送到网络或存储在数据库中&…

UITableView之显示单组数据Demo

需求 UITableView实现显示单组数据。尝试设置不同行高度不同。 效果: 数据展示 实现 与之前分组显示数据的区别在于懒加载的数据模型不同。 (1)声明数据模型类 类的属性一定要和plist中数据的字段保持一致 interface CZhero : NSObject /…

go 语言爬虫库goQuery 的详细使用(知乎日报详情页解析示例)

上一篇《uniapp小程序开发 | 从零实现一款影视类app 》实现了影视小程序的前端和后台接口,虽然包含了大多数小程序应有的知识,但基本还只是涉及网络接口和vue页面的设计。这里介绍下零一个有趣的练手项目,知乎日报。涉及详情页面的html解析&a…

线性稳压器LDO的基础知识

一、什么是线性稳压器? 线性稳压器的工作原理是:采用一个压控电流源以强制在稳压器输出端上产生一个固定电压。控制电路连续监视(检测)输出电压,并调节电流源(根据负载的需求)以把输出电压保持在期望的数值…

【Spine学习12】之 事件帧

1、新建事件帧: 2、选择第8s的攻击帧,点击第一步新建的attack事件帧前面的钥匙 这样每次动作到8s的时候会自动跳出事件帧提示 这个文字实际动画不会显示 事件是动画过程中所发生情况的触发器。 给程序员识别的

分享:2024年(第12届)“泰迪杯”数据挖掘挑战赛省级奖项获奖名单公示

本次竞赛有评选省奖的省份有广东省、广西壮族自治区、河北省、湖北省。各省奖项依据“泰迪杯”全国评审专家组统一评阅的最终成绩区分省份后从高到低依序按比例产生。 广东省 省级奖项获奖名单公示 奖项设置: 一等奖:约占该省份队伍总数的5%&#xff0…

Android开发系列(四)Jetpack Compose之Button

在Jetpack Compose中,Button是一个常用的用户界面组件,用于执行某些操作或触发某些事件。Button控件是可触摸的,并且通常会显示一个文本或图标来表示其功能。 要在Jetpack Compose中创建一个Button,可以使用Button()函数&#xf…

shell学习记录

shell简介 参考博文1 参考博文2——shell语法及应用 参考博文3——vi的使用 在linux中有很多类型的shell,不同的shell具备不同的功能,shell还决定了脚本中函数的语法,Linux中默认的shell是 / b in/ b a s h ,流行的shell有as…

B-splines曲线的绘制(Matlab)

虽然在这个链接三次 Bspline(B样条曲线) NURBS曲线的绘制 matlab_三次b样条曲线的绘制-CSDN博客中我们介绍了NURBS曲线,然而有时候我们通过B-spline曲线也能够解决问题。B-spline曲线作为NURBS曲线的一种特例,这里给出均匀B-spline曲线的表达式&#xff…

基于IDEA的Maven简单工程创建及结构分析

目录 一、用 mvn 命令创建项目 二、用 IDEA 的方式来创建 Maven 项目。 (1)首先在 IDEA 下的 Maven 配置要已经确保完成。 (2)第二步去 new 一个 project (创建一个新工程) (3)…