JS中深拷贝与浅拷贝

在这里插入图片描述

定义

深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是在编程中常用的两种对象复制方式。

浅拷贝(Shallow Copy):

  • 浅拷贝是创建一个新的对象,将原始对象的属性值复制到新对象中。如果属性是基本类型,则直接复制其值;如果属性是引用类型,则复制引用而不是实际对象。
  • 浅拷贝只复制对象的一层,对于嵌套的对象或数组,仅复制它们的引用。因此,当原始对象的属性发生变化时,浅拷贝的对象也会受到影响。
  • 在 JavaScript 中,可以使用 Object.assign() 或扩展运算符 … 来实现浅拷贝。

深拷贝(Deep Copy):

  • 深拷贝是创建一个全新的对象,并递归地将原始对象及其所有嵌套的对象完全复制到新对象中。即使原始对象的属性发生变化,深拷贝的对象也不会受到影响。
  • 深拷贝会遍历整个对象树,复制每个对象的值,并为其创建新的引用。这样可以确保每个对象都是独立的。
  • 在 JavaScript 中,可以使用 JSON 序列化和反序列化方法 JSON.parse(JSON.stringify(obj)) 来实现深拷贝。但需要注意,该方法有一些限制,例如无法复制函数、循环引用等。

使用时需要根据具体的需求来选择使用浅拷贝还是深拷贝。如果只需要复制对象的一层属性,并且不关心原始对象的属性改变对复制后的对象是否产生影响,可以使用浅拷贝。如果需要确保复制后的对象与原始对象完全独立,并且不受原始对象的变化影响,应该使用深拷贝。

深拷贝

方法

常见的深拷贝方式包括:

  1. 手动递归复制:通过编写递归函数,遍历对象的所有属性,并创建新的对象进行复制。对于嵌套的对象或数组,递归调用复制函数。这种方法需要自己处理循环引用和特殊数据类型(如函数)的情况。
function deepCopy(obj) {if (typeof obj !== 'object' || obj === null) {return obj;}let copy = Array.isArray(obj) ? [] : {};for (let key in obj) {if (Object.prototype.hasOwnProperty.call(obj, key)) {copy[key] = deepCopy(obj[key]);}}return copy;
}const obj = {name: "John",age: 25,address: {city: "New York",country: "USA"}
};const newObj = deepCopy(obj);
newObj.address.city = "Los Angeles";console.log(obj);     // { name: "John", age: 25, address: { city: "New York", country: "USA" } }
console.log(newObj);  // { name: "John", age: 25, address: { city: "Los Angeles", country: "USA" } }
  1. JSON 序列化和反序列化:使用 JSON.stringify() 方法将对象转换为字符串,再使用 JSON.parse() 方法将字符串转换回对象。这种方法简单易行,可以实现深拷贝,但无法复制特殊数据类型(如函数、正则表达式)和循环引用。
const obj = {name: "John",age: 25,address: {city: "New York",country: "USA"}
};const newObj = JSON.parse(JSON.stringify(obj));
newObj.address.city = "Los Angeles";console.log(obj);     // { name: "John", age: 25, address: { city: "New York", country: "USA" } }
console.log(newObj);  // { name: "John", age: 25, address: { city: "Los Angeles", country: "USA" } }
  1. 第三方库:许多 JavaScript 的第三方库(如 lodash、jQuery)提供了深拷贝的实现。这些库通常有专门的函数(如 _.cloneDeep()),可以方便地进行深拷贝操作,并且处理了循环引用等特殊情况。
const _ = require('lodash');const obj = {name: "John",age: 25,address: {city: "New York",country: "USA"}
};const newObj = _.cloneDeep(obj);
newObj.address.city = "Los Angeles";console.log(obj);     // { name: "John", age: 25, address: { city: "New York", country: "USA" } }
console.log(newObj);  // { name: "John", age: 25, address: { city: "Los Angeles", country: "USA" } }

使用场景

  1. 对象或数组的修改:当需要对一个对象或数组进行修改,但又不想影响原始数据时,可以使用深拷贝创建一个副本进行修改。

  2. 数据传递和处理:当需要将对象或数组作为参数传递给函数,并且希望在函数内部修改数据时,使用深拷贝可以确保原始数据不会被改变。

  3. 缓存数据:当需要缓存某个对象或数组的状态,并在后续操作中恢复到缓存的状态时,可以使用深拷贝创建一个备份,以便随时恢复。

  4. 避免引用共享:当多个变量需要引用同一个对象或数组时,如果不希望它们共享引用关系,可以使用深拷贝创建它们的独立副本。

  5. 数据比较和检查:当需要比较两个对象或数组是否相等时,可以先使用深拷贝创建它们的副本,然后进行比较操作,以避免引用相等而不实际内容相等的情况。

浅拷贝

方法

常见的浅拷贝方式有以下几种:

  1. 赋值操作(=):通过将一个对象的引用赋给另一个变量,两个变量会引用同一个对象,因此它们共享同一份数据。
const originalObj = { name: 'Alice', age: 30 };
const shallowCopyObj = originalObj;
shallowCopyObj.name = 'Bob';console.log(originalObj);    // { name: 'Bob', age: 30 }
console.log(shallowCopyObj); // { name: 'Bob', age: 30 }
  1. Object.assign() 方法:该方法可以将一个或多个源对象的属性复制到目标对象中,也是浅拷贝,即只拷贝对象的第一层属性。
const originalObj = { name: 'Alice', age: 30 };
const shallowCopyObj = Object.assign({}, originalObj);
shallowCopyObj.name = 'Bob';console.log(originalObj);    // { name: 'Alice', age: 30 }
console.log(shallowCopyObj); // { name: 'Bob', age: 30 }
  1. 扩展运算符(...):该运算符可以将一个对象或数组“展开”为多个参数,相当于使用 Object.assign() 方法进行拷贝。
const originalArray = [1, 2, 3];
const shallowCopyArray = [...originalArray];
shallowCopyArray[0] = 4;console.log(originalArray);    // [1, 2, 3]
console.log(shallowCopyArray); // [4, 2, 3]

使用场景

  1. 简单的数据结构:当需要复制简单的数据结构,例如基本类型、普通对象或一维数组时,可以使用浅拷贝。

  2. 不需要修改原始数据:当不需要对原始对象或数组进行修改,并且只是希望获取一个副本用于读取或传递给其他函数时,可以使用浅拷贝。

  3. 大规模数据集合:当处理大规模的数据集合时,为了节省内存和提高性能,可以使用浅拷贝来创建一份数据的快照,以备后续操作使用。

  4. 对象或数组的扁平化:当需要将嵌套的对象或多维数组转换为一维结构时,可以使用浅拷贝来创建一个扁平化的副本。

  5. 实现对象合并:当需要将多个对象的属性合并到一个新对象中时,可以使用浅拷贝将多个对象合并成一个对象。

区别

相同点

  • 都是用于复制对象或数组的方法,可以创建原始数据的副本。
  • 在特定情况下都可以用于避免原始数据被修改影响到副本的情况。

不同点

  • 拷贝层级:深拷贝会递归复制对象或数组的所有属性或元素,包括嵌套的子对象或数组,而浅拷贝只复制第一层的属性或元素,不会递归复制嵌套结构。
  • 引用关系:深拷贝创建的是新的独立对象或数组,与原始数据没有任何关联,而浅拷贝复制的是对象或数组的引用,副本与原始数据共享部分引用。
  • 数据完整性:深拷贝保留原始数据的完整性,复制的是完整的数据结构,而浅拷贝无法保留原始数据的完整性,对于嵌套结构只复制引用。
  • 对原始数据的影响:深拷贝创建的副本修改不会影响原始数据,而浅拷贝创建的副本修改可能会影响原始数据。
import copy# 原始数据
original_list = [1, 2, [3, 4]]# 浅拷贝
shallow_copy = copy.copy(original_list)# 修改浅拷贝后的数据
shallow_copy[2][0] = 5# 打印原始数据和浅拷贝后的数据
print(original_list)  # 输出:[1, 2, [5, 4]]
print(shallow_copy)   # 输出:[1, 2, [5, 4]]

在这个示例中,我们使用copy.copy()函数进行浅拷贝。结果表明,虽然我们修改了浅拷贝后的数据,但原始数据也发生了变化。这是因为浅拷贝只复制了原始列表的引用,而没有递归地复制嵌套列表。

import copy# 原始数据
original_list = [1, 2, [3, 4]]# 深拷贝
deep_copy = copy.deepcopy(original_list)# 修改深拷贝后的数据
deep_copy[2][0] = 5# 打印原始数据和深拷贝后的数据
print(original_list)  # 输出:[1, 2, [3, 4]]
print(deep_copy)      # 输出:[1, 2, [5, 4]]

在这个示例中,我们使用copy.deepcopy()函数进行深拷贝。结果表明,当我们修改深拷贝后的数据时,原始数据没有受到影响。这是因为深拷贝递归地复制了所有嵌套的列表,创建了原始数据的完整副本。

利弊

深拷贝具有以下利与弊:

利:

  1. 复制对象的完整数据结构:深拷贝会递归复制对象或数组的属性或元素,包括嵌套的子对象或数组,可以复制对象的完整数据结构,保留原始数据的完整性。

  2. 独立副本:深拷贝会创建新的独立对象或数组,而不是共享引用,它们与原始数据没有任何关联,修改副本不会影响原始数据。

  3. 处理循环引用:深拷贝可以处理对象内部存在循环引用的情况,避免了无限递归或栈溢出等问题。

弊:

  1. 复制性能较低:由于深拷贝需要递归复制对象或数组的所有属性或元素,可能会消耗大量的时间和内存资源,对性能造成一定影响。

  2. 可能存在对象方法失效问题:在深拷贝过程中,如果对象或数组的属性或元素中包含函数或方法,那么这些函数或方法可能会在复制后失效,这可能会导致应用程序出现异常或错误。

浅拷贝具有以下利与弊:

利:

  1. 简单快速:浅拷贝是一种简单且高效的复制方式,操作简单快速。

  2. 节省内存:由于浅拷贝只复制第一层属性或元素,而不会递归复制嵌套的子对象或数组,因此可以节省内存空间。

  3. 保留原始引用关系:浅拷贝会保留原始对象或数组的引用关系,即拷贝后的对象或数组与原始对象或数组共享相同的引用,可以在一定程度上减少数据冗余。

弊:

  1. 共享引用关系:由于浅拷贝只复制引用,而不是创建新的独立对象或数组,所以当修改拷贝后的对象或数组时,原始对象或数组也会受到影响,这可能导致意外的副作用。

  2. 不保留原始数据完整性:浅拷贝只复制第一层属性或元素,对于嵌套的子对象或数组,只复制它们的引用,而不是创建新的独立对象或数组,因此无法保留原始数据的完整性。

  3. 无法处理循环引用:如果原始对象存在循环引用,即对象内部的属性或元素相互引用形成闭环,浅拷贝无法处理此类情况,可能会导致无限递归或栈溢出等问题。

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

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

相关文章

Smart Link和Monitor Link

Smart Link和Monitor Link简介 Smart Link,又叫做备份链路。一个Smart Link由两个接口组成,其中一个接口作为另一个的备份。Smart Link常用于双上行组网,提供可靠高效的备份和快速的切换机制。 Monitor Link是一种接口联动方案,它…

nodejs流

什么是流 stream 流是用于在 Node.js 中处理流数据的抽象接口。 node:stream 模块提供了用于实现流接口的 API。 什么是流数据 流数据是指一组顺序、大量、快速、连续到达的数据序列,一般情况下数据流可被视为一个随时间延续而无限增长的动态数据集合。流数据应用…

【keil备忘录】2. stm32 keil仿真时的时间测量功能

配置仿真器Trace内核时钟为单片机实际的内核时钟,需要勾选Enable设置,设置完成后Enable取消勾选也可以,经测试时钟频率配置仍然生效,此处设置为48MHZ: 时间测量时必须打开register窗口,否则可能不会计数 右下角有计…

第十四章 : Spring Boot 整合spring-session,使用redis共享

第十四章 : Spring Boot 整合spring-session,使用redis共享 前沿 本文重点讲述:spring boot工程中使用spring-session机制进行安全认证,并且通过redis存储session,满足集群部署、分布式系统的session共享。 基于SPringBoot 2.3.2…

[linux运维] 利用zabbix监控linux高危命令并发送告警(基于Zabbix 6)

之前写过一篇是基于zabbix 5.4的实现文章,但是不太详细,最近已经有两个小伙伴在zabbix 6上操作,发现触发器没有str函数,所以更新一下本文,基于zabbix 6 0x01 来看看效果 高危指令出发问题告警: 发出邮件告…

学好操作系统需要的前置知识

1. 态度:不要等一切都准备好了再前行 如果把一切你可能会说,没有这些基础知识,我每看一篇文章,知识就铺天盖地席卷过来,仿佛每一个知识点都准确地打在了自己的盲点上,这该怎么办呢? 我非常能理…

AI助力智慧农业,基于YOLOv8全系列模型【n/s/m/l/x】开发构建不同参数量级的识别系统

智慧农业随着数字化信息化浪潮的演变有了新的定义,在前面的系列博文中,我们从一些现实世界里面的所见所想所感进行了很多对应的实践,感兴趣的话可以自行移步阅读即可: 《自建数据集,基于YOLOv7开发构建农田场景下杂草…

05 JQuery基础入门

文章目录 一、jQuery介绍1. 简介2. 版本介绍3. 相关网站4. HTML引入方式 二、基础语法1. 顶级对象$2. 与DOM对象转化3. 选择器4. 事件5. 动画6. 修改样式7. 修改属性 一、jQuery介绍 1. 简介 jQuery是JavaScript编程语言底层库,它是一个快速,简洁的Jav…

ERPNext SQL 注入漏洞复现

0x01 产品简介 ERPNext 是一套开源的企业资源计划系统。 0x02 漏洞概述 ERPNext 系统frappe.model.db_query.get_list 文件 filters 参数存在 SQL 注入漏洞,攻击者除了可以利用 SQL 注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在高权…

基于springboot实现的仿天猫商城项目

一、系统架构 前端:jsp | js | css | jquery 后端:springboot | mybatis-plus 环境:jdk1.7 | mysql | maven 二、代码及数据库 三、功能介绍 01. web端-首页 02. web端-商品查询 03. web端-商品详情 04. web端-购物车 05. web端-订单…

集合的几个遍历方法

1. 集合的遍历 1.0 创建集合代码 List<String> strList new ArrayList<>(); strList.add("huawei"); strList.add("xiaomi"); strList.add("tencent"); strList.add("google"); strList.add("baidu");1.1 fo…

复杂gRPC之go调用go

1. 复杂的gRPC调用 我们使用了一个较为复杂的proto文件&#xff0c;这个文件的功能主要是用来定位的&#xff0c;详细内容可以看代码中的注解 syntax "proto3"; //指定生成的所属的package&#xff0c;方便调用 option go_package "./"; package route…

Redis和MySQL双写一致性实用解析

1、背景 先阐明一下Mysql和Redis的关系&#xff1a;Mysql是数据库&#xff0c;用来持久化数据&#xff0c;一定程度上保证数据的可靠性&#xff1b;Redis是用来当缓存&#xff0c;用来提升数据访问的性能。 关于如何保证Mysql和Redis中的数据一致&#xff08;即缓存一致性问题…

labelme等标注工具/数据增强工具输出JSON文件格式检查脚本

标注的文件太多了&#xff0c;还有用数据增强工具生成了一票的新数据。在转换或使用训练时候会报错&#xff0c;错误原因是json中语法有问题&#xff0c;这样会中断程序运行&#xff0c;调试造成很大困扰。 检查确实最后有问题&#xff0c;多写了一次 写一个脚本&#xff0c;用…

Python-滑雪大冒险【附源码】

滑雪大冒险 《滑雪大冒险》是一款充满趣味性和挑战性的休闲竞技游戏&#xff0c;在游戏中&#xff0c;玩家将扮演一位勇敢的滑雪者&#xff0c;在雪山上展示他们的滑雪技巧&#xff0c;游戏采用2D图形界面&#xff0c;以第三人称视角呈现 运行效果&#xff1a;用方向键及方向键…

flask 数据库迁移可能出现的六大问题,生成requirements文件夹方式,flask项目复写,

今日任务 项目分级显示 — app — — admin 代表 — — auth 代表用户的点赞 评论 登录等等 — — blog 代表blog的网页 首先单独把auth运行出来 第一步 1. 生成requirements文件夹 2.在一个新的虚拟环境里面完成requirements依赖下载 3.完成项目的复写 1. 生成requ…

算术运算(这么简单?进来坐坐?)

先热热身 算术运算&#xff0c;也称为四则运算&#xff0c;包括加法、减法、乘法和除法。此外&#xff0c;算术运算还包括乘方和开方。 在算术中&#xff0c;加减被视为一级运算&#xff0c;乘除被视为二级运算&#xff0c;乘方和开方被视为三级运算。在一道算式中&#xff0c;…

网站导航栏下滑隐藏,上滑显示,效果杠杆,兼容性强

前言 导航栏是网站必不可少的一部分&#xff0c;那么&#xff0c;导航栏应该怎么样子实现&#xff0c;可以高效自定义兼容开发呢&#xff1f;当然&#xff0c;不仅要实现&#xff0c;而且还要实现导航栏顶部固定位置&#xff0c;下拉隐藏&#xff0c;稍微往上滑动就会出现&…

Python中的并发编程(2)线程的实现

Python中线程的实现 1. 线程 在Python中&#xff0c;threading 库提供了线程的接口。我们通过threading 中提供的接口创建、启动、同步线程。 例1. 使用线程旋转指针 想象一个场景&#xff1a;程序执行了一个耗时较长的操作&#xff0c;如复制一个大文件&#xff0c;我们希…

2022年第十一届数学建模国际赛小美赛D题野生动物贸易是否应长期禁止解题全过程文档及程序

2022年第十一届数学建模国际赛小美赛 D题 野生动物贸易是否应长期禁止 原题再现&#xff1a; 野生动物市场被怀疑是此次疫情和2002年SARS疫情的源头&#xff0c;食用野生肉类被认为是非洲埃博拉病毒的一个来源。在冠状病毒爆发后&#xff0c;中国最高立法机构永久性地加强了野…