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

news2025/1/21 18:53:13

前言

当谈及拷贝,你的第一印象会不会和我一样,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.coloradmin.cn/o/1837649.html

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

相关文章

【机器学习】第11章 神经网络与深度学习(重中之重)

一、概念 1.神经元模型 (1)神经网络的基本组成单位 (2)生物上,每个神经元通过树突接受来自其他被激活神经元的信息,通过轴突释放出来的化学递质改变当前神经元内的电位。当神经元内的电位累计到一个水平时…

【Docker实战】jenkins卡在编译Dockerfile的问题

我们的项目是标准的CI/CD流程,也即是GitlabJenkinsHarborDocker的容器自动化部署。 经历了上上周的docker灾难,上周的服务器磁盘空间灾难,这次又发生了jenkins卡住的灾难。 当然,这些灾难有一定的连锁反应,是先发生的d…

2024 端午节巽寮湾游玩记录

2024 端午节巽寮湾游玩记录 文章目录 2024 端午节巽寮湾游玩记录一、前言二、巽寮湾游玩行程1、三天衣食住行2、主要图片: 三、其他1、小结2、巽寮湾游玩建议3、感慨 一、前言 时间总是过得很快,只要你活着时间就会不停往前走。 所以你以后的路其实都是…

【机器学习】 第1章 概述

一、概念 1.机器学习是一种通过先验信息来提升模型能力的方式。 即从数据中产生“模型”( model )的算法,然后对新的数据集进行预测。 2.数据集(Dataset):所有数据的集合称为数据集。 训练集:用来训练出一个适合模…

Maven添加reactor依赖失败

目录 情况说明 解决过程 情况说明 起初是自己在学spring boot3&#xff0c;结果到了reactor这一部分的时候&#xff0c;在项目的pom.xml文件中添加下列依赖报错&#xff1a; <dependencyManagement><dependencies><dependency><groupId>io.projectr…

“Git掌控:分布式版本控制系统解析“

目录 # Git基础 1. Git下载 2. Git三种程序命令 2.1 Git Bash基本命令 2.2 Git更新项目命令 3. Git配置 3.1 设置用户名&#xff08;EE配置&#xff09; 3.2 查看用户名信息 4. Git基本理论 5. Git项目搭建 5.1 克隆远程仓库 5.2 指定本地克隆的目录 6. Git文件操…

【硬件开发】自举电路

为什么需要自举电路 半桥驱动电路中&#xff0c;下桥臂的源极直接接地&#xff0c;导通时栅极电压只需要达到MOS管导通d电压Vgs&#xff0c;但是对于上桥臂来说&#xff0c;上桥臂的源极电压会随着上桥臂的导通而抬升到VDD&#xff0c;对应想要导通时的栅极电压Vgs要到达VgsVDD…

代码随想录第20天|二叉树

654.最大二叉树 构造二叉树: 使用前序遍历 已理解思路 617.合并二叉树 虽然开辟额外空间, 但结果依旧受到原来的数影响(当为null时直接借用了原来数的节点) class Solution { public:TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {if (root1 nullptr) return roo…

ubuntu20.04配置anaconda

1.anaconda下载 地址&#xff1a;Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 选择&#xff1a;Anaconda3-2023.07-2-Linux-x86_64.sh 2.anaconda安装 选择下载目录&#xff0c;选在在终端中打开&#xff0c;然后在终端输入安装命令…

【漏洞复现】海康威视 综合安防管理平台 session接口 远程代码执行漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

关机充电动画:流程与定制

关机充电动画&#xff1a;流程与定制 基于MTK平台Android 11分析 生成logo.bin 关机充电动画是由一系列的bmp图片组成的&#xff0c;这些图片资源存在于vendor/mediatek/proprietary/bootable/bootloader/lk/dev/logo目录下&#xff08;当然不仅保护关机充电动画&#xff0c…

北漂新媒体从业者的一天

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 本文纯属虚构 小李通过努力考上了一所北京的大学&#xff0c;毕业后就留在了北京&#xff0c;做新媒体运营&#xff0c;在天通苑租了一个单间&#xff0c;月租2500&#xff0c;业余时间做自媒体&#xff0c;也算是…

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…

如何编辑扫描的 PDF 文档

无论您从事哪个行业&#xff08;医疗保健、房地产、现场服务管理或金融服务&#xff09;&#xff0c;您都很有可能使用在线表格和调查来培训员工、收集客户反馈、授权付款和注册活动参与者。使用合适的软件&#xff0c;这些表格很容易创建、编辑和填写。此外&#xff0c;您还可…

【文献阅读】Partially Adaptive Array Techniques

Abstract 文章研究了在多窄带干扰环境下&#xff0c;辅助阵元的选择&#xff0c;为部分自适应天线阵&#xff0c;以达到性能优化的目的。推导了双干扰问题的显式解。这个案例足以说明多个干扰的相互作用&#xff0c;同时也为更复杂的问题提供了一定程度的理解。本文还提出并讨…

如何为自己的项目生成changelog

背景 在github上看到人家的更新日志感觉很cool&#xff0c;怎么能给自己项目来一套呢 环境信息 tdstdsdeMacBook-Pro demo-doc % node -v v14.18.1 tdstdsdeMacBook-Pro demo-doc % npm -v 6.14.15硬件信息 型号名称&#xff1a;MacBook Pro版本&#xff1a; 12.6.9芯片&…

nginx反向代理动静分离和负载均衡

一.nginx 反向代理简要介绍 1.什么是反向代理 反向代理是一种服务器&#xff0c;在这种设置中&#xff0c;代理服务器接收客户端的请求&#xff0c;并将这些请求转发给一个或多个后端服务器&#xff08;例如应用服务器、数据库服务器等&#xff09;。然后&#xff0c;后端服务…

Transformer预测 | 基于Transformer+LSTM股票价格预测时间序列预测(Pytorch)

效果一览 基本介绍 股票行情是引导交易市场变化的一大重要因素,若能够掌握股票行情的走势,则对于个人和企业的投资都有巨大的帮助。然而,股票走势会受到多方因素的影响,因此难以从影响因素入手定量地进行衡量。但如今,借助于机器学习,可以通过搭建网络,学习一定规模的股…

Unity URP下通过相机让部分Render不受后处理渲染

我们有时候不想某些对象受到后处理影响&#xff0c;找到了这样一个决绝办法&#xff0c;通过增加一个Overlay相机只照射这个模型来实现&#xff0c;下面看看如何实现。 第一步 首先我们拖一个测试场景&#xff0c;有如下一些元素 一个盒子&#xff0c;以后后处理&#xff0c…

海思SS928/SD3403开发笔记1——使用串口调试开发板

该板子使用串口可以调试&#xff0c;下面是win11 调试 该板子步骤 1、给板子接入鼠标、键盘、usb转串口 2、下载SecureCRT&#xff0c;并科学使用 下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/11dIkZVstvHQUhE8uS1YO0Q 提取码&#xff1a;vinv 3、安装c…