什么是拷贝?我: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.rhkb.cn/news/353512.html

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

相关文章

Vue10-实战快速上手

实战快速上手 我们采用实战教学模式并结合ElementUI组件库,将所需知识点应用到实际中,以最快速度带领大家掌握Vue的使用; 1、创建工程 注意:命令行都要使用管理员模式运行 1、创建一个名为hello-vue的工程vue init webpack hel…

iview 组件里面的(任何一个月)整月日期全部选中_iview时间轴选中有历史记录日期

iview 组件里面的整月日期全部选中: ①:第一种是当前月的日期全部选中: 先上效果图:当前月分 获取到的值: 当前月的方法: // getDateStr() {// var curDate new Date();// var curMonth curDate.ge…

【HTML01】HTML基础-基本元素-附带案例-作业

文章目录 HTML 概述学HTML到底学什么HTML的基本结构HTML的注释的作用html的语法HTML的常用标签:相关单词参考资料 HTML 概述 英文全称:Hyper Text Markup Language 中文:超文本标记语言,就将常用的50多个标记嵌入在纯文本中&…

python pytest 参数化的几种方式

一、使用pytest.mark.parametrize装饰器: 可以使用pytest提供的pytest.mark.parametrize装饰器来指定参数化测试的参数。下面是一个示例: import pytest# pytest.mark.parametrize装饰器 # 其中num expected,分别对应(1, 1),(2, 4),(3, 9)&…

c语言中的字符函数

1.字符分类函数 c语言中有一系列函数是专门做字符分类的&#xff0c;也就是一个字符属于什么类型的字符。这些函数的使用需要包含一个头文件是ctype.h 可能你看这些感觉很懵&#xff0c;我以islower举例 #include<ctype.h> int main() {int retislower(A);printf("…

Git记录 上传至Gitee

1.GitHub拉去的代码需要上传至自己的Gitee需要清除原有remote服务器信息 查看原始远程服务器信息&#xff0c;后删除远程服务器信息 git remote -v git remote rm origin 2.Gitee新建软件仓库 法1&#xff09;不用初始化仓库&#xff0c;初始化会自动生成.git。如果本地.git…

Qt项目天气预报(3) - qt的http编程获取天气数据

概念 Qt中的HTTP编程主要涉及使用Qt的网络模块来进行HTTP请求和处理HTTP响应。Qt提供了一系列类来处理网络通信&#xff0c;其中最常用的类是 QNetworkAccessManager 、 QNetworkRequest 、 QNetworkReply 以及相关的支持类。 编程实例 以下是一个基本的HTTP编程示例&#xff0…

LVGL开发教程-objects对象

知不足而奋进 望远山而前行 目录 知不足而奋进 望远山而前行​ 文章目录 前言 1.图层 2.objects 2.1 位置 2.2 尺寸 2.3 align 2.4 样式 总结 前言 在嵌入式 GUI 开发中&#xff0c;LVGL&#xff08;Light and Versatile Graphics Library&#xff09;是一个强大的工…

HTTP性能测试工具-wrk

wrk性能测试工具详解 wrk是一款轻量级但功能强大的HTTP基准测试工具&#xff0c;主要用于在单机多核CPU环境下对HTTP服务进行性能测试。它通过利用系统自带的高性能I/O机制&#xff08;如epoll、kqueue等&#xff09;&#xff0c;结合多线程和事件模式&#xff0c;能够产生大量…

MAGs培养有线索了?宏组学中未培养微生物表型与培养条件预测

宏基因组测序技术让人们对地球上微生物的多样性有了更深入的了解&#xff0c;但分离培养是研究微生物的生理代谢功能并解析其生态作用的关键。2023年11月的世界微生物数据中心&#xff08;WDCM&#xff09;年会中&#xff0c;全面启动了全球“未培养微生物培养组”计划&#xf…

27、matlab傅里叶变换:fft()函数

1、傅里叶变换简介 傅里叶变换是数学中一种非常重要的工具&#xff0c;用于将一个函数&#xff08;通常是时域函数&#xff09;分解成一组正弦和余弦函数的和。通过傅里叶变换&#xff0c;可以将一个信号从时域转换到频域&#xff0c;以便更好地理解信号的频率成分和频谱特征。…

[保姆级教程]uniapp实现页面路由配置

文章目录 新建目录新建页面配置页面路由修改tabBar地址其他&#xff1a;在package.json中的pages配置详细 新建目录 先点击src–》新建–》目录 输入名称&#xff0c;并以此类推完成所有新建目录 新建页面 右击目录&#xff0c;点击新建–》vue文件 弹出弹框&#xff0c;…

JVM性能优化工具及问题排查

jvm性能优化工具 jdk提供给我们了很实用的工具来分析JVM的状态&#xff0c;线程以及配置&#xff0c;这些工具包含于jdk中&#xff0c;并且以java实现&#xff0c;是JVM性能优化必不可少的工具集&#xff0c;这些工具都在$JAVA_HOME/bin下 jps、jinfo、jstack、jmap、jstat基本…

力扣793. 阶乘函数后 K 个零

Problem: 793. 阶乘函数后 K 个零 文章目录 题目描述思路即解法复杂度Code 题目描述 思路即解法 1.根据题意可知即是要求取满足条件的n最小是多少&#xff0c;最大是多少&#xff0c;最大值和最小值一减&#xff0c;就可以算出来有多少个n满足条件了。 2.由于题目中的阶乘存在单…

springboot连接多个库

一个SpringBoot项目&#xff0c;同时连接两个数据库&#xff1a;比如一个是Mysql数据库&#xff0c;一个是oracle数据库&#xff08;啥数据库都一样&#xff0c;连接两个同为oracle的数据库&#xff0c;或两个不同的数据库&#xff0c;只需要更改对应的driver-class-name和jdbc…

读AI新生:破解人机共存密码笔记05逻辑

1. 困难问题 1.1. 管理政府或教授分子生物学之类的问题要困难得多 1.2. 这些环境很复杂&#xff0c;大部分是不可观察的&#xff08;一个国家的状态&#xff0c;一个学生的思想状态&#xff09;&#xff0c;还有更多的对象和对象类型&#xff0c;对动作…

云上战场:ToDesk云电脑、青椒云、顺网云全面对决

前言 记得端午放假期间&#xff0c;我已经在旅游的路上了&#xff0c;老板突然发短信&#xff0c;让我给他画个图&#xff0c;我手上的战斗机已经是十年前的老古董了(平常一直用的公司电脑&#xff0c;也没想过要换)&#xff0c;压根满足不了老板的任务要求&#xff0c;不得已&…

了解振弦采集仪在建筑物安全监测中的应用与研究

了解振弦采集仪在建筑物安全监测中的应用与研究 摘要&#xff1a;河北稳控科技振弦采集仪是一种常用的结构物安全监测设备&#xff0c;广泛应用于建筑物、桥梁、塔楼等工程结构的监测。本文将从振弦采集仪的原理、应用案例和研究进展等方面进行详细介绍&#xff0c;以便更好地…

ClickHouse安装与下载22.3.2.2

ClickHouse安装与下载 目录 1. ClickHouse简介 1.1 ClickHouse优点&#xff1a; 1.2 ClickHouse缺点&#xff1a; 1.3 ClickHouse引擎&#xff1a; 1.3.1 数据库引擎 1.3.2 表引擎 2. ClickHouse下载安装 2.1 ClickHouse下载安装 2.2 ClickHouse使用 1. ClickHouse简…

2024最新IDEA插件开发+发布全流程 SelectCamelWords[选中驼峰单词](idea源代码)

2024最新IDEA插件开发&#xff08;发布&#xff09;-SelectCamelWords[选中驼峰单词]&#xff08;idea源代码&#xff09; 参考文档 Jetbrains Idea插件开发文档: https://plugins.jetbrains.com/docs/intellij/welcome.html代码地址&#xff1a;https://github.com/yangfeng…