什么是深拷贝;深拷贝和浅拷贝有什么区别;深拷贝和浅拷贝有哪些方法(详解)

目录

一、为什么要区别深拷贝和浅拷贝

二、浅拷贝

2.1、什么是浅拷贝

2.2、浅拷贝的方法

使用Object.assign()

使用展开运算符(...)

使用数组的slice()方法(仅适用于数组)

2.3、关于赋值运算符(=)

三、深拷贝

3.1、什么是深拷贝

3.2、实现深拷贝的三种常见方法

使用JSON.parse(JSON.stringify(obj))

使用第三方库,如lodash的cloneDeep()

手动实现递归函数

四、总结


一、为什么要区别深拷贝和浅拷贝

        在JavaScript中,对象和数组是引用类型,这意味着当你将它们赋值给一个变量或者作为函数参数传递时,你实际上是在传递一个指向内存中对象的引用,而不是对象本身的副本。这就涉及到了深拷贝和浅拷贝的概念。

二、浅拷贝

2.1、什么是浅拷贝

        浅拷贝只复制对象的第一层属性。也就是说,如果原始对象的属性是基本类型(如数字、字符串、布尔值),那么浅拷贝会复制这些值;但如果属性是引用类型(如数组、对象、函数),那么拷贝的只是引用,而不复制引用指向的内存地址中的数据。

2.2、浅拷贝的方法

使用Object.assign()
let original = { a: 1, b: { c: 2 } };
let shallowCopy = Object.assign({}, original);original.a = 3;
original.b.c = 4;console.log(shallowCopy.a) // 1 ; original.a是original的第一层属性,值是一个数字,所以浅拷贝到shallowCopy中还是一个数值,不受original影响console.log(shallowCopy.b.c) // 4 ; original.b.c是original的第二层属性,第一层属性original.b的值是一个引用类型,浅拷贝只会拷贝引用,所以修改original.b.c也会修改shallowCopy.b.c
使用展开运算符(...)
let original = { a: 1, b: { c: 2 } };
let shallowCopy = { ...original }; // 使用展开运算符进行浅拷贝original.a = 3;
original.b.c = 4;console.log(shallowCopy.a) // 1 ; original.a是original的第一层属性,值是一个数字,所以浅拷贝到shallowCopy中还是一个数值,不受original影响console.log(shallowCopy.b.c) // 4 ; original.b.c是original的第二层属性,第一层属性original.b的值是一个引用类型,浅拷贝只会拷贝引用,所以修改original.b.c也会修改shallowCopy.b.c
使用数组的slice()方法(仅适用于数组)
let originalArray = [1, 2, 3, [4, 5]];
let shallowCopyArray = originalArray.slice();originArray.push(6);
originArray[3].push(5);console.log(originalArray);   // 输出: [1, 2, 3, [4, 5, 5], 6] 
console.log(shallowCopyArray); // 输出: [1, 2, 3, [4, 5, 5]]let originalArray = [1, 2, 3, [4, 5]];
let shallowCopyArray = originalArray.slice();
originArray.push(6);
originArray[3].push(5);
originArray[3] = [4, 5];console.log(originalArray);   // 输出: [1, 2, 3, [4, 5], 6] 
console.log(shallowCopyArray); // 输出: [1, 2, 3, [4, 5, 5]] 因为 [4, 5]是一个新数组,是将新的引用赋给了originArray[3],并不会直接改变shallowCopyArray[3]的引用

2.3、关于赋值运算符(=)

        在JavaScript中,赋值运算符(=)本身并不执行浅拷贝或深拷贝。赋值运算符只是将一个值或一个引用赋给一个变量。如果赋值的是一个基本数据类型(如数字、字符串、布尔值),那么赋值运算符会复制这个值。如果赋值的是一个引用类型(如对象、数组、函数),那么赋值运算符会复制这个引用,而不是引用指向的对象本身。

        当使用赋值运算符来处理对象和数组时,没有创建一个新的对象/数组,两个变量指向的是同一个对象/数组,所以不管修改哪一个对象/数组的任何一层的任何一个数据,另外一个都会随之改变。(连浅层/第一层的数值都没有拷贝,完全依赖引用)

let a = 10; // a是基本数据类型
let b = a;  // b复制了a的值,a和b现在是两个独立的数字console.log(a === b); // true,因为它们都是数字10let obj1 = { key: 'value' };
let obj2 = obj1; // obj2复制了obj1的引用obj2.key = 'new value'; // 修改obj2的key属性console.log(obj1.key); // 输出 'new value',因为obj1和obj2引用的是同一个对象
console.log(obj1 === obj2); // true,因为它们引用的是同一个对象let obj3 = { key: 'value' , deep:{a:5 , b:6}};
let obj4 = obj1; // obj2复制了obj1的引用
obj3.deep.a = 6console.log(obj4.deep.a); //6

三、深拷贝

3.1、什么是深拷贝

        深拷贝会创建一个全新的对象,并且递归地复制所有属性,使得原始对象和拷贝对象完全独立,互不影响。深拷贝后的对象中所有的属性都是新的引用,即使属性是引用类型,也会被完全复制。

        实现深拷贝的方法相对复杂,因为需要递归地复制对象的所有层级。以下是一些常见的实现方式

3.2、实现深拷贝的三种常见方法

使用JSON.parse(JSON.stringify(obj))

        这是最简单的一个方法,实际开发中经常会用到,不过虽然简单但有局限性,不能处理函数、undefined、循环引用、特殊对象(如Date、RegExp)等。

let original = { a: 1, b: { c: 2 } };
let deepCopy = JSON.parse(JSON.stringify(original));
使用第三方库,如lodash的cloneDeep()
let original = { a: 1, b: { c: 2 } };
let deepCopy = JSON.parse(JSON.stringify(original));
手动实现递归函数

        实现深拷贝的递归函数需要考虑多种情况,包括但不限于:

  • 检测对象是否为基本类型。
  • 处理循环引用。
  • 复制数组和对象。
  • 处理特殊对象(如Date、RegExp)。

        举个例子:

function deepCopy(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);let result;if (Array.isArray(obj)) {result = [];hash.set(obj, result);for (let i = 0; i < obj.length; i++) {result[i] = deepCopy(obj[i], hash);}} else {result = Object.create(Object.getPrototypeOf(obj));hash.set(obj, result);for (let key in obj) {if (obj.hasOwnProperty(key)) {result[key] = deepCopy(obj[key], hash);}}}return result;
}

四、总结

        浅拷贝适用于只复制对象的第一层属性,且这些属性不是引用类型。深拷贝适用于需要完全独立的副本,包括对象和数组的嵌套结构。选择哪种拷贝方式取决于你的具体需求和场景。

        意犹未尽?更多精彩前端好文请关注:各种前端问题的技巧和解决方案

        博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

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

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

相关文章

selenium使用已经打开的浏览器

Selenium 本身不支持直接连接到一个已经打开的浏览器页面。Selenium 启动的浏览器实例是一个全新的会话&#xff0c;它与手动打开的浏览器页面是分开的。但是&#xff0c;有一些变通的方法可以实现类似的效果。 一种方法是通过附加代理连接到已经打开的浏览器。下面是如何实现…

解决:GoLand能断点,但无法下一步debug | 下一步按钮是灰的

目录 1. 背景2. 解决方案 1. 背景 突然发现goLand能断点成功&#xff0c;但是无法debug下一步&#xff0c;又急&#xff0c;网上一下子没找到解决方案&#xff0c;最后花了好多时间&#xff0c;打印了好多日志才定位到代码问题所在&#xff0c;后面花时间研究了一下&#xff0…

C++ 20新特性之线程与jthread

&#x1f4a1; 如果想阅读最新的文章&#xff0c;或者有技术问题需要交流和沟通&#xff0c;可搜索并关注微信公众号“希望睿智”。 为什么要引入jthread 在C 11中&#xff0c;已经引入了std::thread。std::thread为C标准库带来了一流的线程支持&#xff0c;极大地促进了多线程…

leetcode第709题:转换成小写字母

注意字符不仅有26个英文字母&#xff0c;还有特殊字符。特殊字符的话&#xff0c;原样输出。 public class Solution {public char toLowChar(char c){if(c>a&&c<z){return c;}else if(c>A&&c<Z){int n(int)c32;return (char)n;}return c;}publi…

Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)

前言&#xff1a;ArrayList是Java中最常用的动态数组实现之一&#xff0c;它提供了便捷的操作接口和灵活的扩展能力&#xff0c;使得在处理动态数据集合时非常方便。本文将深入探讨Java中ArrayList的实现原理、常用操作以及一些使用场景。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨…

useEffect的概念以及使用(对接口)

// useEffect的概念以及使用 import {useEffect, useState} from reactconst Url"http://geek.itheima.net/v1_0/channels"function App() {// 创建状态变量const [lustGet,setLustGet]useState([]);// 渲染完了之后执行这个useEffect(() > {// 额外的操作&#x…

软考初级网络管理员__标准化基础知识单选题

1.张某购买了一张有注册商标的应用软件光盘&#xff0c;擅自复制出售&#xff0c;则其行为侵犯了()。 注册商标专用权 光盘所有权 软件著作权 软件著作权与商标权 2.以下关于软件著作权产生的时间&#xff0c;表述正确的是()。 自软件首次公开发表时 自开发者有开发意图…

如何进行两表数据合并-即包含两张表的所有数据

如果第二张表的数据量多于第一张表&#xff0c;并且您希望最终的表包含两张表的所有数据&#xff0c;即使某些数据在一张表中不存在&#xff0c;可以使用FULL OUTER JOIN。然而&#xff0c;需要注意的是&#xff0c;MySQL不支持FULL OUTER JOIN&#xff0c;但是可以通过结合LEF…

红队攻防渗透技术实战流程:组件安全:SolrShirolog4j

红队攻防渗透实战 1. 组件安全1.1 Solr1.1.1 命令执行(CVE-2019-17558)1.1.2 远程命令执行漏洞(CVE-2019-0193)1.1.3 Apache Solr 文件读取&SSRF (CVE-2021-27905)1.2 Shiro:1.2.1 CVE_2016_4437 Shiro-550+Shiro-7211.2.2 CVE-2020-119891.2.3 CVE-2020-19571.2.4 CVE-…

【TypeScript】泛型工具

跟着 小满zs 学 ts&#xff1a;学习TypeScript24&#xff08;TS进阶用法-泛型工具&#xff09;_ts泛型工具-CSDN博客 Partial 所有属性可选的意思Required 所有属性必选的意思Pick 提取部分属性Exclude 排除部分属性emit 排除部分属性并且返回新的类型 Partial 属性变为可选。…

Qt-Advanced-Docking-System的学习

Qt5.12实现Visual Studio 2019 拖拽式Dock面板-Qt-Advanced-Docking-System_c_saide6000-GitCode 开源社区 (csdn.net) 我使用的是Qt5.5.0 开始&#xff0c;我下载的是最新版的源码&#xff1a;4.1版本 但是&#xff0c;打开ads.pro工程文件&#xff0c;无法编译成功。 然后…

RERCS系统开发实战案例-Part02 创建BOPF对应的业务对象(Business Object)

1、通过事务码 BOBF创建业务对象 2、输入debug&#xff0c;进入编辑模式新建BO对象&#xff1b; 选择对应的BO对象属性类别&#xff1a; 3、激活BO对象 接口页签&#xff1a; 属性页签&#xff1a;自动带出标准的常量 改接口类部分源码&#xff1a; 4、BO对象Node Elemen…

Golang的Gin框架

目录 功能以及简单使用 gin.Engine数据结构 RouterGroup methodTrees gin.context 功能以及简单使用 功能: • 支持中间件操作&#xff08; handlersChain 机制 &#xff09; • 更方便的使用&#xff08; gin.Context &#xff09; • 更强大的路由解析能力&#xff08…

Web前端在深圳:探索技术与创新的融合之地

Web前端在深圳&#xff1a;探索技术与创新的融合之地 在数字化浪潮席卷全球的今天&#xff0c;深圳作为中国最具创新活力的城市之一&#xff0c;其在Web前端领域的发展同样引人注目。本文将从四个方面、五个方面、六个方面和七个方面&#xff0c;深入剖析深圳在Web前端领域的独…

Linux之tar打包解包命令

Linux之tar打包解包命令 打包与压缩区别 打包&#xff0c;也称之为归档&#xff0c;指的是一个文件或目录的集合&#xff0c;而这个集合被存储在一个文件中。归档文件没有经过压缩&#xff0c;所占空间是其中所有文件和目录的总和。 压缩&#xff0c;将一个大文件通过压缩算法…

基于 Delphi 的前后端分离:之五,使用 HTMX 让页面元素组件化之面向对象的Delphi代码封装

前情提要 本博客上一篇文章&#xff0c;描述了使用 Delphi 作为后端的 Web Server&#xff0c;前端使用 HTMX 框架&#xff0c;把一个开源的前端图表 JS 库&#xff0c;进行了组件化。 上一篇文章仅仅是描述了简单的前端代码组件化的可能性&#xff0c;依然是基于前端库的 JS…

windows下的eclipse按Ctrl+Shift+F格式化代码不起作用的处理

1、先上张图&#xff1a; 上面Format&#xff1a;CtrlShiftF&#xff0c;按了以后不起作用。 2、这个快捷键不起作用的原因&#xff1a;可能是快捷键冲突了。 机器上装了Sougou输入法&#xff0c;将输入法切换为英文模式是起作用的。 那么应该就是这个原因了。 3、解决方法…

【Ardiuno】使用ESP32单片机创建web服务通过网页控制小灯开关的实验(图文)

经过实验测试ESP32单片机的网络连接还是很方便的&#xff0c;这里小飞鱼按照程序实例的代码亲自实验一下使用Esp32生成的网页服务来实现远程无线控制小灯开关功能&#xff0c;这样真的是离物联网开发越来越近了&#xff0c;哈哈&#xff01; 连接好开发板和电路&#xff0c;将…

React入门教程:构建你的第一个React应用

在当今快速发展的Web开发领域&#xff0c;前端技术日新月异&#xff0c;而React作为一款强大的JavaScript库&#xff0c;已经成为众多开发者的首选。React以其组件化、高效的性能和灵活的数据处理机制闻名于世&#xff0c;被广泛用于构建动态且复杂的用户界面。在本教程中&…

Elixir学习笔记——输入输出和文件系统

本章介绍输入/输出机制、文件系统相关任务以及相关模块&#xff08;如 IO、File 和 Path&#xff09;。IO 系统提供了一个很好的机会来阐明 Elixir 和 Erlang VM 的一些思维模式和新奇思想。 输入输出模块 输入输出模块是 Elixir 中读写标准输入/输出 (:stdio)、标准错误 (:s…