当前位置: 首页 > news >正文

js数组/对象的深拷贝与浅拷贝


文章目录

  • 一、js中的深拷贝和浅拷贝
  • 二、浅拷贝
    • 1、Object.assign()
    • 2、利用es6扩展运算符(...)
  • 二、深拷贝
    • 1、JSON 序列化和反序列化
    • 2、js原生代码实现
    • 3、使用第三方库lodash等
  • 四、总结


一、js中的深拷贝和浅拷贝

在JS中,深拷贝和浅拷贝是针对对象(Object)和数组(Array)这类复杂数据类型复制时的概念。

  • 浅拷贝: 当进行浅拷贝时,只是将对象或数组的引用复制一份给新的变量。这意味着新旧变量指向的是同一个内存空间中的数据结构,修改其中一个变量会影响到另一个变量,因为它们实际上是共享同一份数据。

  • 深拷贝: 则是创建一个与原对象完全独立的对象副本,对于原对象内部所包含的所有层级的数据(包括嵌套的对象或数组),都会递归地进行复制,从而保证了新旧对象间不存在任何共享的引用。

简单来说,对拷贝后的数组或对象进行修改,会影响原数组或对象的就是浅拷贝,不会影响的就是深拷贝。

二、浅拷贝

1、Object.assign()

Object.assign() 基本用法:将一个或多个源对象的属性复制到目标对象,并返回目标对象。这一过程是浅拷贝的,即对于嵌套对象或数组,只是拷贝了引用而非创建新的对象。
示例:

const obj = {a: 1,b: {c: 2},d: ["e"]
};
const cloneObj = Object.assign({}, obj);
cloneObj.b.c = 3; // 修改拷贝后的对象console.log("obj: ", obj);
console.log("cloneObj: ", cloneObj);

结果:
在这里插入图片描述
从结果可以看到,修改 clonedObj 的属性也会影响到原始对象 obj,所以Object.assign()实现的是浅拷贝。

2、利用es6扩展运算符(…)

在ES6中,...运算符被称为扩展(Spread)运算符,可以用于数组和对象的浅拷贝。
示例1:拷贝对象

const obj2 = {a: 1,b: {c: 2},d: ["hello"]
};
const cloneObj2 = { ...obj2 };
cloneObj2.b.c = 2222222;console.log("obj2: ", obj2);
console.log("cloneObj2: ", cloneObj2);

结果:
在这里插入图片描述

示例2:拷贝数组

const arr = ["1", { a: 2 }, 3, true, undefined];
const arrClone = [...arr];
arrClone[0] = "first"; // 修改数组第1个元素
arrClone[1].a = "hello"; // 修改数组第2个元素console.log("arr: ", arr);
console.log("arrClone: ", arrClone);

结果:
在这里插入图片描述

...拷贝数组结果来看,原始数组和拷贝数组是独立的,但由于数组中包含的对象引用并未改变,所以修改拷贝数组中对象的属性时,原始数组中对应的对象也会受到影响。

也就是说,对于简单数组,...可以实现数组深拷贝,但是复杂数组…则不能用做实现深拷贝的方法。

二、深拷贝

1、JSON 序列化和反序列化

利用 JSON 的序列化和反序列化可以实现对象的深拷贝。原理是:JSON.stringify()会递归遍历对象的所有属性(包括嵌套的对象和数组),将其转换为JSON字符串;然后JSON.parse()则会根据JSON字符串创建新的JavaScript对象。

示例:

const obj1 = {a: 1,b: {c: 2},d: ["hello"]
};
const cloneObj1 = JSON.parse(JSON.stringify(obj1));
cloneObj1.b.c = 3;console.log("obj1: ", obj);
console.log("cloneObj1: ", cloneObj);

结果:
在这里插入图片描述
从结果看到,修改拷贝后的对象其中的属性值,并不会改变原对象的属性值,所以实现的是一个深拷贝。

!!!注意这种方法有局限:

  • 它不能处理函数和RegExp等非JSON兼容类型。
  • 对象中的循环引用会导致错误或丢失数据。
  • 如果对象中有undefined、function或symbol类型的属性,它们在序列化过程中会被忽略。
  • 对日期对象,JSON.stringify会将日期转换为字符串,所以反序列化后得到的是字符串而不是Date对象,如果需要保持日期类型,需要额外处理。

2、js原生代码实现

  • 简单版:只考虑普通对象属性,不考虑内置对象和函数
	function deepClone1(obj) {if (typeof obj !== "object") return;let newObj = obj instanceof Array ? [] : {};for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] =typeof obj[key] === "object" ? deepClone1(obj[key]) : obj[key];}}return newObj;}// 测试
const obj5 = {a: 1,b: {c: 2},d: ["hello"],e: new Date(),f: new Error("error"),g: new RegExp("/^(.*\..{4}).*$/")
};
const cloneObj5 = deepClone1(obj5);
cloneObj5.b.c = "hhhhhh";console.log("obj5: ", obj5);
console.log("cloneObj5: ", cloneObj5);

结果:
在这里插入图片描述

  • 【推荐】复杂版:考虑内置对象比如Date、RegExp等对象和函数以及解决循环引用的问题。
// 判断是否为object类型
function isObject(target) {return ((typeof target === "object" && target) || typeof target === "function");}
function deepClone(data, map = new WeakMap()) {// 基础类型直接返回值if (!isObject(data)) {return data;}// 日期或者正则对象则直接构造一个新的对象返回if ([Date, RegExp].includes(data.constructor)) {return new data.constructor(data);}// 处理函数对象if (typeof data === "function") {return new Function("return " + data.toString())();}// 如果该对象已存在,则直接返回该对象const exist = map.get(data);if (exist) {return exist;}// 处理Map对象if (data instanceof Map) {const result = new Map();map.set(data, result);data.forEach((val, key) => {// 注意:map中的值为object的话也得深拷贝if (isObject(val)) {result.set(key, deepClone(val));} else {result.set(key, val);}});return result;}// 处理Set对象if (data instanceof Set) {const result = new Set();map.set(data, result);data.forEach(val => {if (isObject(val)) {// 注意:set中的值为object的话也得深拷贝result.add(deepClone(val));} else {result.add(val);}});return result;}// 收集键名(考虑了以Symbol作为key以及不可枚举的属性)const keys = Reflect.ownKeys(data);// 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性以及对应的属性描述const allDesc = Object.getOwnPropertyDescriptors(data);// 结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链, 这里得到的result是对data的浅拷贝const result = Object.create(Object.getPrototypeOf(data), allDesc);// 新对象加入到map中,进行记录map.set(data, result);// Object.create()是浅拷贝,所以要判断并递归执行深拷贝keys.forEach(key => {const val = data[key];if (isObject(val)) {// 属性值为 对象类型 或 函数对象 的话也需要进行深拷贝result[key] = deepClone(val);} else {result[key] = val;}});return result;
}// 测试
const obj4 = {a: 1,b: {c: 2},d: ["hello"],e: new Date(),f: new Error("error"),g: new RegExp("/^(.*\..{4}).*$/")
};
const cloneObj4 = deepClone(obj4);
cloneObj4.b.c = "hhhhhh";console.log("obj4: ", obj4);
console.log("cloneObj4: ", cloneObj4);

结果:
在这里插入图片描述

3、使用第三方库lodash等

例如:import { cloneDeep } from 'lodash'; 这种拿来就可以用的方法就不做过多介绍了~

四、总结

总的来说,js中的深、浅拷贝其实就是一个基本功,在实际业务场景中,可根据数据复杂程度自行选择各种实现方式,加油⛽️

相关文章:

js数组/对象的深拷贝与浅拷贝

文章目录 一、js中的深拷贝和浅拷贝二、浅拷贝1、Object.assign()2、利用es6扩展运算符(...) 二、深拷贝1、JSON 序列化和反序列化2、js原生代码实现3、使用第三方库lodash等 四、总结 一、js中的深拷贝和浅拷贝 在JS中,深拷贝和浅拷贝是针对…...

HCIA学习第六天:OSPF:开放式最短路径优先协议

OSPF:开放式最短路径优先协议 无类别链路状态IGP动态路由协议 1.距离矢量协议:运行距离矢量协议的路由器会周期性的泛洪自己的路由表。通过路由的交互,每台路由器从相邻的路由器学习到路由,并且加载进自己的路由表中&#xff1b…...

从四个方面来解决企业在项目管理中遇到的各类问题

案例背景:某建筑集团有限公司成立于1949年,拥有国家房屋建筑工程施工总承包一级、建筑装修装饰工程专业承包一级、市政公用工程施工总承包一级资质。是一家集建筑施工、设备安装、装饰装潢、仿古建筑、房地产开发、建材试验为一体的具有综合生产能力的建…...

使用代码取大量2*2像素图片各通道均值,存于Excel文件中。

任务是取下图RGB各个通道的均值及标签(R, G,B,Label),其中标签由图片存放的文件夹标识。由于2*2像素图片较多,所以将结果放置于Excel表格中,之后使用SVM对他们进行分类。 from PIL import Image import os …...

React16源码: React中commit阶段的commitBeforeMutationLifecycles的源码实现

commitBeforeMutationLifecycles 1 )概述 在 react commit 阶段的 commitRoot 第一个while循环中调用了 commitBeforeMutationLifeCycles现在来看下,里面发生了什么 2 )源码 回到 commit 阶段的第一个循环中,在 commitRoot 函数…...

压制二元组的总价值

压制二元组的总价值 对于每一个 a i a_i ai​, 看它能压制它前面的多少个元素, 那么它对总价值的贡献就是: 在a数组中: a i a_i ai​压制了x个数, 贡献为: x ∗ i x*i x∗i被 a i a_i ai​所压制的所有数在 a a a中的下标和为 y y y, 贡献为 − y -y −y 树状数组来求: 为了…...

【习题】保存应用数据

判断题 1. 首选项是关系型数据库。 错误(False) 2. 应用中涉及到Student信息,如包含姓名,性别,年龄,身高等信息可以用首选项来存储。 错误(False) 3. 同一应用或进程中每个文件仅存在一个Preferences实例。 正确(True) 单选题 …...

Flask框架小程序后端分离开发学习笔记《5》简易服务器代码

Flask框架小程序后端分离开发学习笔记《5》 Flask是使用python的后端,由于小程序需要后端开发,遂学习一下后端开发。 简易服务器代码 接口解析那一块很关键,学后端服务器这一块,感觉主要就是学习相应地址的接口怎么处理。 然后…...

“计算机视觉处理设计开发工程师”专项培训(第二期)

“人工智能技术与咨询” 发布...

R语言学习case7:ggplot基础画图(核密度图)

step1: 导入ggplot2库文件 library(ggplot2)step2&#xff1a;带入自带的iris数据集 iris <- datasets::irisstep3&#xff1a;查看数据信息 dim(iris)维度为 [150,5] head(iris)查看数据前6行的信息 step4&#xff1a;画图展示 plot2 <- ggplot(iris,aes(Sepal.W…...

Ubuntu18配置Docker

1.基本过程 1.更新软件源列表 sudo apt update2.安装软件包依赖 sudo apt install apt-transport-https ca-certificates curl software-properties-common3.在系统中添加Docker的官方密钥 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - …...

Keil/MDK平台 - 结构体成员指针注意事项

文章目录 1 . 前言总结2 . 问题现象3 . 解决思路4 . 细节扩展5 . 总结 【极客技术传送门】 : https://blog.csdn.net/Engineer_LU/article/details/135149485 1 . 前言总结 有时候希望通过类定义的类型指向数据包来解析&#xff0c;恰好又想结构体内定义指针指向一段数据&…...

一款超级好用的远程控制APP,你值得拥有

在这个科技日新月异的时代&#xff0c;我们的生活被各种手机软件所包围。几乎每个人都有一个甚至多个手机&#xff0c;你是否也有遇到过需要远程操作自己某一台手机的场景呢&#xff1f;今天&#xff0c;我要向大家推荐一款神奇的手机远程操作神器&#xff0c;让你可以随时随地…...

NumPy必知必会50例 | 18. 使用 NumPy 解决线性方程组:数学问题的实用解决方案

继续我们的 NumPy 探索之旅吧&#xff0c;接下来我们将探讨使用 NumPy 解决线性方程组&#xff0c;一种实用的数学应用。 文章目录 18. 使用 NumPy 解决线性方程组&#xff1a;数学问题的实用解决方案线性方程组&#xff1a;数学世界的基石创建线性方程组 解决实际问题应用场景…...

C/C++编码问题研究

文章目录 一、Unicode字符集与U8/U16/U32编码二、编码1. 占字节数2. ASCII、GB2312、GBK、GB18030 以及 UTF8 的关系3. BOM4. UTF-8的存储实现 三、编译器字符集设置1. GCC语法Example 2. MSVC语法Example 三、wchar_t五、编码转换函数六、代码 & 实践1. UTF8与UTF16、UTF3…...

二刷代码随想录|Java版|回溯算法3|子集问题

习题 2.3 子集问题 就是组合过程收集path。就像是代码随想录里说得那样&#xff0c;组合和分割问题就是收集叶子结点&#xff0c;子集问题就是收集每一个节点。 有涉及到同层重复元素的问题。 先排序&#xff0c;后再for循环里处理相同数值跳过。 设置函数内的used。 还可以用…...

mongodb config

windows&#xff1a; 1.同级bin&#xff0c;data&#xff0c;log创建mongo.config文件 dbpathD:\Program\mongodb\data\db logpathD:\Program\mongodb\log\mongo.log logappendtrue #默认启用日志 journaltrue #过滤无用日志信息&#xff0c;调试设置为false quiettrue port2…...

pytorch 实现中文文本分类

&#x1f368; 本文为[&#x1f517;365天深度学习训练营学习记录博客&#x1f366; 参考文章&#xff1a;365天深度学习训练营&#x1f356; 原作者&#xff1a;[K同学啊 | 接辅导、项目定制]\n&#x1f680; 文章来源&#xff1a;[K同学的学习圈子](https://www.yuque.com/mi…...

【MySQL】聚合函数和内置函数

文章目录 1 :peach:聚合函数:peach:2 :peach:group by子句的使用:peach:3 :peach:内置函数:peach:3.1 :apple:日期函数:apple:3.2 :apple:字符串函数:apple:3.3 :apple:数学函数:apple: 4 :peach:其它函数:peach: 1 &#x1f351;聚合函数&#x1f351; 函数说明COUNT([DISTIN…...

python第五节:集合set(4)

集合其他方法&#xff1a; len(s) set 的长度 x in s x 是否是 s 的成员 x not in s x 是否不是 s 的成员 s.issubset(t) 是否 s 中的每一个元素都在 t 中 s.issuperset(t) 是否 t 中的每一个元素都在 s s.union(t) 返回一个新的 set 包含 s 和 t 中的每一个元素 …...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...