js的深拷贝与浅拷贝

一、深拷贝与浅拷贝的区别

1、浅拷贝

浅拷贝只复制对象的一层属性。如果属性值是基本数据类型(如字符串、数字、布尔值等),则直接复制其值;如果属性值是引用数据类型(如数组、对象等),则复制其引用(即地址),而不是复制实际的对象或数组本身。因此,在浅拷贝后,原始对象和复制对象会共享相同的引用类型属性。

举个例子:

let original = { a: 1, b: { c: 2 } };
let shallowCopy = Object.assign({}, original);shallowCopy.b.c = 20;
console.log(original.b.c); // 输出:20

在上面的例子中,修改 shallowCopy 的属性 b.c 同时也改变了 original 中对应的值,因为它们指向同一个内存地址。

2、深拷贝

与浅拷贝相反,深拷贝会复制对象的所有层级,创建一个完全独立的副本。这意味着原始对象和复制对象不会共享任何引用类型的属性。无论多么复杂的对象结构,深拷贝都会递归复制其所有层级,生成一个结构完全相同但完全独立的对象。

let original = { a: 1, b: { c: 2 } };
// 假设deepClone是一个有效的深拷贝函数
let deepCopy = deepClone(original); deepCopy.b.c = 20;
console.log(original.b.c); // 输出:2

在这个例子中,修改 deepCopy 的属性 b.c 不会影响到 original 中对应的值,因为 deepCopyoriginal 的一个完全独立的副本。

总结差异

  • 浅拷贝只复制对象的第一层属性,而深拷贝会复制对象的所有层级。
  • 浅拷贝中,如果原始对象的属性值是引用类型,则复制的是这个引用,而不是引用的值本身,导致原始对象和复制对象的这些属性实际上是共享的。
  • 深拷贝创建了一个完全独立的对象副本,不共享任何属性或嵌套的引用,因此更加占用内存空间,并可能涉及更复杂的复制过程。

二、浅拷贝的使用

浅拷贝的方法有多种。这些技术主要复制对象的一级属性,如果属性值是引用类型(例如数组或其他对象),则复制的是引用地址而不是真实的独立对象

1. 使用展开运算符(Spread Operator)

const original = { a: 1, b: { c: 2 } };
const copy = { ...original };

展开运算符 ... 可以用来复制对象的所有可枚举属性到一个新的对象中。

2. 使用 Object.assign()

const original = { a: 1, b: { c: 2 } };
const copy = Object.assign({}, original);

Object.assign() 方法可以用来将所有可枚举属性的值从一个或多个源对象复制到目标对象,并返回目标对象。

3. 数组的 slice() 方法

对于数组,可以使用 slice() 方法实现浅拷贝。

const originalArray = [1, 2, { a: 3 }];
const copiedArray = originalArray.slice();

这里的 slice() 方法没有指定开始和结束参数,因此会返回原数组的一个拷贝。但请注意,对于数组内部的对象,拷贝的仍然是引用。

4. 数组的 concat() 方法

const originalArray = [1, 2, { a: 3 }];
const copiedArray = originalArray.concat();

slice() 类似,concat() 在这种用法下也会返回数组的一个浅拷贝。注意的是: 数组中都是简单数据类型,可以说是深拷贝,如果数组中有复杂数据类型,它就浅拷贝

5. 使用Array.from()对数组进行浅拷贝

const originalArray = [1, 2, { a: 3 }];
const copiedArray = Array.from(originalArray);

Array.from() 方法可以从一个类似数组或可迭代的对象创建一个新的、浅拷贝的数组实例。

注意事项

以上方法都能实现浅拷贝,但它们只针对对象的第一层属性。如果对象中包含嵌套对象,则这些嵌套对象不会被复制,复制的只是嵌套对象的引用。因此,修改原始对象或复制对象中的嵌套对象可能会影响到另一个对象。

三、深拷贝的使用

1. JSON 方法

利用 JSON.stringify()JSON.parse() 可以快速实现一个对象的深拷贝。但是,它不支持复制函数、循环引用等。

function deepClone(obj) {return JSON.parse(JSON.stringify(obj));
}const obj = { a: 1, b: { c: 2 } };
const clonedObj = deepClone(obj);
console.log(clonedObj); // { a: 1, b: { c: 2 } }

注意:但它有几个明显的弊端,这些限制使得它不能在所有情况下都适用:

(1)丢失函数:如果对象中含有函数,这些函数不会被克隆。JSON.stringify() 在遇到函数时会将其忽略,因此在克隆后的对象中,这些位置将不再有任何函数存在。

const obj = {data: "data",func: function() { console.log(this.data); }
};
const clonedObj = JSON.parse(JSON.stringify(obj));
// clonedObj 不包含 func 属性。

(2)无法复制特殊对象:某些特殊的对象类型(如 Date, RegExp, Map, Set 等)在经过 JSON.stringify()JSON.parse() 处理后会失去其原始类型信息,变成普通的对象或者其他结构。

const obj = { date: new Date() };
const clonedObj = JSON.parse(JSON.stringify(obj));
// clonedObj.date 变为了字符串,而不是一个 Date 对象。

(3)循环引用问题:如果对象具有循环引用,即对象直接或间接地引用自身,则 JSON.stringify() 会抛出错误,因为它无法处理循环引用的情况。

const obj = {};
obj.self = obj;
// 这里会抛出错误
const clonedObj = JSON.parse(JSON.stringify(obj));

(4)忽略 Symbol 属性:由于 JSON.stringify() 不能序列化 Symbol 类型的键名,这些属性会被完全忽略,在克隆的对象中不会保留。

(5)忽略 undefined、Function 和 RegExp:undefined 以及函数和正则表达式等值在序列化过程中会被忽略或者改变。

(6)忽略原型链:克隆的对象不会保持同一个原型链,克隆后的对象是一个纯净的字面量对象,没有继承任何原型方法。

2. 使用递归

这种方法允许你更细致地控制各种数据类型如何被复制,并且可以处理循环引用的问题。

function deepClone(obj, hash = new WeakMap()) {if (obj === null) return null;if (typeof obj !== "object") return obj;if (obj instanceof Date) return new Date(obj);if (obj instanceof RegExp) return new RegExp(obj);if (hash.has(obj)) return hash.get(obj);const cloneObj = new obj.constructor();hash.set(obj, cloneObj);for (const key in obj) {if (obj.hasOwnProperty(key)) {cloneObj[key] = deepClone(obj[key], hash);}}return cloneObj;
}const complexObj = {numbers: [1, 2, 3],person: {name: "John",address: {city: "New York",coordinates: {lat: 40.7128,lng: -74.0060}}}
};const clonedComplexObj = deepClone(complexObj);console.log(clonedComplexObj);
/* 输出:
{numbers: [ 1, 2, 3 ],person: {name: 'John',address: { city: 'New York', coordinates: { lat: 40.7128, lng: -74.006 } }}
}
*/
console.log(clonedComplexObj.person === complexObj.person); // 输出: false
console.log(clonedComplexObj.person.address === complexObj.person.address); // 输出: false

3. 使用 structuredClone()

从JavaScript的某些版本开始,提供了structuredClone()方法,这是一个执行深拷贝的原生方法,也能很好地处理各种内置类型、循环引用等。

const obj = { a: 1, b: { c: 2 } };
const clonedObj = structuredClone(obj);
console.log(clonedObj); // { a: 1, b: { c: 2 } }

4. 利用第三方库Lodash

  • 使用 lodash 的 _.cloneDeep() 方法:
const _ = require('lodash');
const obj = { a: 1, b: { c: 2 } };
const clonedObj = _.cloneDeep(obj);
console.log(clonedObj); // { a: 1, b: { c: 2 } }

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

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

相关文章

Python实战:使用DrissionPage库爬取高考网大学信息

上一篇文章,我刚入门 DrissionPage 爬虫库,使用这个库爬取了拉钩网关于 Python 的职位信息。 今天再使用 DrissionPage 爬虫库练习一个案例,爬取高考网大学信息。 本次爬取到2885个大学信息,包含大学名称、所在省、市、大学标签信…

Git私服搭建

1、安装 openssh服务器 sudo apt-get install openssh-server openssh-client 2、创建个人公钥和私钥 在默认用户的主目录路径下,运行以下命令,按照提示创建公钥和私钥 ssh-keygen -t rsa 默认生成2048位,如果需要提高安全级别&#xff0c…

Galah:一款功能强大的LLM驱动型OpenAI Web蜜罐系统

关于Galah Galah是一款功能强大的Web蜜罐,该工具由LLM大语言模型驱动,基于OpenAI API实现其功能。 很多传统的蜜罐系统会模拟一种包含了大量网络应用程序的网络系统,但这种方法非常繁琐,而且有其固有的局限性。Galah则不同&…

【算法详解 | 二分查找】详解二分查找 \ 折半查找高效搜索算法 | 顺序数组最快搜索算法 | 递归循环解决二分查找问题

二分查找 by.Qin3Yu 本文需要读者掌握 顺序表 的操作基础,完整代码将在文章末尾展示。 顺序表相关操作可以参考我的往期博文: 【C数据结构 | 顺序表速通】使用顺序表完成简单的成绩管理系统.by.Qin3Yu 文中所有代码使用 C 举例,且默认已使用…

DAO设计模式

概念:DAO(Data Access Object) 数据库访问对象,**面向数据库SQL操作**的封装。 (一)场景 问题分析 在实际开发中,针对一张表的复杂业务功能通常需要和表交互多次(比如转账)。如果每次针对表的…

微服务系统设计:横向扩展和纵向扩展的对比

微服务扩展性:水平扩展 vs 垂直扩展 特点水平扩展垂直扩展扩展单位增加微服务实例增加单个实例的资源 (CPU,内存)方向向外,增加节点向上,增加单个节点的资源复杂性随着实例数量的增加,管理难度更大管理更简单&#xf…

聊一聊Tomcat的架构和运行流程,尽量通俗易懂一点

1、Tomcat的架构 这里可以看出 A、一个Tomcat就是一个Server,一个Server下会有多个Service, B、Service只负责封装多个Connector和一个Container(Service本身不是容器,可以看做只是用来包装Connector和Container的壳&#xff0c…

基于Springboot的社区疫情防控平台

末尾获取源码作者介绍:大家好,我是墨韵,本人4年开发经验,专注定制项目开发 更多项目:CSDN主页YAML墨韵 学如逆水行舟,不进则退。学习如赶路,不能慢一步。 一、项目简介 以往的社区疫情防控管理…

交强险投保日期查询接口返回字段说明

API接口是现代互联网应用中重要的组成部分,通过接口的调用可以实现不同系统之间的数据交互和共享。在汽车保险行业中,交强险投保日期查询接口是非常关键的一个接口,本文将详细介绍该接口的返回字段和使用方法。 接口名称:交强险投…

【Java基础】自定义类型处理器xxxTypeHandler

1. 背景 在处理复杂的数据类型时,我们一般需要提供一种灵活的映射机制,以适应数据库字段和Java实体类之间的差异。 例: dim_bu_info_private_i表中有一个json类型的字段extend_info create table dim_bu_info_private_i (id …

计算机网络(第六版)复习提纲23

第五章:运输层 SS5.1 运输层协议概述 1 进程之间的通信 A 运输层要实现复用和分用: 复用:发送方不同进程都能使用用一个运输层协议传送数据 分用:接收方的运输层在剥去报文首部后能将这些数据交付给正确的应用 B 运输层提供应用进…

17 # 类型检查机制:类型保护

例子: enum Type {Strong,Week }class Java {helloJava(){console.log(hello Java);} }class JavaScript {helloJavaScript(){console.log(hello JavaScript);} }function getLanguage(type: Type){let lang type Type.Strong ? new Java() : new JavaScript();…

算法专题:记忆搜索

参考练习习题总集 文章目录 前置知识练习习题87. 扰乱字符串97. 交错字符串375. 猜数字大小II403. 青蛙过河464. 我能赢吗494. 目标和552. 学生出勤记录II576. 出借的路径数 前置知识 没有什么特别知识,只有一些做题经验。要做这类型的题目,首先写出暴…

C++入坑基础知识点

当学习了C语言之后,很多的小伙伴都想进一步学习C,但两者有相当一部分的内容都是重叠的,不知道该从哪些方面开始入门C,这篇文章罗列了从C到C必学的入门知识,学完就算是踏入C的大门了。 1. 命名空间 写C的时候&#xff…

使用Vue-Grid-Layout实现自定义工作台

前言 当代工作环境要求高度的个性化和定制化,因此,自定义工作台成为了一个热门的需求。Vue-Grid-Layout是一个强大的Vue组件,可以帮助我们实现自定义工作台的功能。在本篇博客中,我将向您介绍如何使用Vue-Grid-Layout来创建一个自…

找不到d3dcompiler_43.dll,无法继续执行代码的原因分析与解决方法

在运行某些软件或游戏时,可能会遇到系统提示找不到 d3dcompiler_43.dll 文件的情况。这个特定的动态链接库文件 (dll) 是 DirectX 3D 编译器组件的一部分,对于许多现代软件游戏的正常运行起着不可或缺的作用。它的主要功能在于将高级着色语言编写的代码转…

零基础学Python之核心基础知识

1.Python入门简介 (1)什么是Python Life is short, you need Python!人生苦短,我用Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性,相比其他语言…

MySQL数据库入门(概念+使用)

目录 1. 数据库的概念 1.1 数据库的存储介质 1.2 主流数据库 2. MySQL的基本使用 2.1 链接数据库 2.2 服务器管理 2.3 数据库,服务器和表关系 2.4 简单MySQL语句 3. MySQL架构 4. SQL分类 5. 存储引擎 本篇完。 1. 数据库的概念 数据库是按照数据结构来…

【CSS】页面自适应屏幕宽度(响应式布局媒体查询-@media、弹性布局、网格布局和相对单位-vh/em/%)

【CSS】页面自适应屏幕宽度(响应式布局媒体查询-media、弹性布局、网格布局和相对单位-vh/em/%) 一、媒体查询(media)1、媒体类型2、媒体特征3、媒体查询语法4、示例(1)示例1(2)示例…

筛选可疑密码

题目描述 情报小组截获了若干个可疑密码,这些密码都是4位数。现在他们获得了一条最新情报:个位数与千位数的和 减去十位数与百位数的和,结果是一个正数。请你帮助情报组筛选目前的可疑密码。 输入 输入两行,第一行是&#xff1…