彻底理解浅拷贝和深拷贝

目录

  • 浅拷贝
    • 实现
  • 深拷贝
    • 实现
    • 自己手写

浅拷贝

浅拷贝是指创建一个新对象,这个对象具有原对象属性的精确副本

  • 基本数据类型(如字符串、数字等),在浅拷贝过程中它们是通过值传递的,而不是引用传递,修改值并不会影响原对象

  • 如果这些属性是引用类型(如对象、数组等),浅拷贝只会复制它们的引用,而不会复制它们的内容

  • 浅拷贝后的新对象和原对象中的引用类型属性仍然指向相同的内存地址,修改其中一个的引用类型数据,会影响另一个

实现

  • Object.assign(target, source):用于将所有可枚举的属性从一个或多个源对象复制到目标对象,返回目标对象

    const obj = {name: "obj",age: 18,height: 180,o: {a: 1,b: 2,},
    };
    const copy = Object.assign({ height: 188 }, obj);
    console.log(copy); // {height: 180, name: 'obj', age: 18, o:{...}}
    copy.name = "copy";
    console.log(obj.name); // obj
    console.log(copy.name); // copy
    copy.o.a = 11;
    console.log(copy.o); // {a: 11, b: 2}
    console.log(obj.o); // {a: 11, b: 2}
    
  • 数组的 slice() 方法:对于数组,slice() 方法可以用来进行浅拷贝。它返回一个新数组,并将原数组中的元素逐个复制到新数组中,但如果数组的元素是对象,它们仍然共享相同的引用

    const names = ["abc", "def", { name1: "ghi", name2: "cba" }];
    const copy = names.slice(1);
    console.log(copy); // ['def', {name1: 'ghi', name2: 'cba'}]
    copy[1].name1 = "abc";
    console.log(copy[1]); // {name1: 'abc', name2: 'cba'}
    console.log(names[2]); // {name1: 'abc', name2: 'cba'}
    
  • 扩展运算符(spread operator ...):适用于数组和对象

    const obj = {name: "obj",age: 18,height: 180,o: {a: 1,b: 2,},
    };
    const names = ["abc", "def", { name1: "ghi", name2: "cba" }];
    const copy1 = { ...obj };
    const copy2 = [ ...names ];
    copy1.o.a = 11;
    console.log(copy1.o); // {a: 11, b: 2}
    console.log(obj.o); // {a: 11, b: 2}copy2[1].name1 = "abc";
    console.log(copy2[2]); // {name1: 'abc', name2: 'cba'}
    console.log(names[2]); // {name1: 'abc', name2: 'cba'}
    

实现的图解:

在这里插入图片描述

浅拷贝对基本数据类型有效,但对于对象、数组等引用类型,只是复制了它们的引用,这会导致在修改拷贝时,原对象也被修改。如果需要对嵌套对象和数组进行完全独立的拷贝,则需要使用深拷贝

深拷贝

深拷贝是指将一个对象的所有属性都完整地复制到另一个对象中,包括嵌套的对象或数组。深拷贝与浅拷贝不同,浅拷贝只复制对象的引用,而深拷贝会递归地复制对象的所有层次,确保原始对象和新对象完全独立,任何一方的修改不会影响另一方

实现

  • JSON实现:这是最简单的一种方式,适合处理不包含函数、undefinedSymbol、循环引用等特殊类型的对象,序列化有问题的情况如下:

    • undefined 不会被序列化,且在对象属性值中会被删除,在数组中则会被转化为 null

    • Symbol 是唯一的标识符,无法被序列化,且会被丢弃

    • Date 对象会被序列化为字符串,但当反序列化时,它不再是 Date 对象,而是一个普通字符串

    • 如果对象有循环引用,JSON.stringify 会抛出错误,因为它无法处理递归结构

    • MapSet 结构会被序列化为空对象,并且在反序列化时,无法恢复为原始结构

    • 不会序列化对象的原型链属性,因此对象的继承关系会丢失

    const set = new Set();
    const obj = {name: "obj",age: 18,height: undefined,o: {a: 1,b: 2,},[Symbol()]: "symbol",[set]: set,date: new Date()
    };
    console.log(JSON.parse(JSON.stringify(obj))); // {name: 'obj', age: 18, o: {…}, [object Set]: {}, date: "2024-09-14T06:48:44.497Z"}
    
  • 使用 structuredClone():在一些现代浏览器中,可以使用内置的 structuredClone() 来实现深拷贝。它可以处理大多数情况下的深拷贝需求,包括循环引用、DateMapSet

    • 不能拷贝 Symbol属性Symbol 类型属性会被忽略,因为 Symbol 是唯一的标识符,具有不可枚举性和唯一性

    • 拷贝 Symbol 值会报错Failed to execute 'structuredClone' on 'Window': Symbol() could not be cloned

    • 不会拷贝对象的原型链属性

    const obj = {name: "Alice",age: undefined,[Symbol()]: "symbol", // 会忽略
    };const clone = structuredClone(obj);
    console.log(clone); // { name: 'Alice', age: undefined }
    
  • 使用 Lodash 库中的 _.cloneDeep():Lodash 是一个非常流行的 JavaScript 工具库,其中提供了 _.cloneDeep() 方法,可以轻松实现深拷贝,对于大部分普通对象、数组、SetMap能够正确处理并进行深拷贝

    • 无法深拷贝 Symbol 属性,但可以克隆对象中以 Symbol 作为值的属性
    const sym = Symbol('id');
    const obj = {[sym]: 'value',id: Symbol('id'),name: 'Alice'
    };
    const clone = _.cloneDeep(obj);
    console.log(clone); // { id: Symbol(id), name: 'Alice' } -- Symbol 属性被忽略,Symbol 值被正确克隆
    

自己手写

  • 实现对对象和基本数据类型的拷贝
  • Symbolkey 进行处理
  • 其他数据类型的值进程处理:数组、函数、SymbolSetMap
  • 对循环引用的处理
function deepCopy(originValue, map = new WeakMap()) {// 0.如果值是Symbol的类型if (typeof originValue === "symbol") {return Symbol(originValue.description)}// 1.如果是原始类型, 直接返回if (!isObject(originValue)) {return originValue}// 2.如果是set类型if (originValue instanceof Set) {const newSet = new Set()for (const setItem of originValue) {newSet.add(deepCopy(setItem))}return newSet}// 3.如果是函数function类型, 不需要进行深拷贝if (typeof originValue === "function") {return originValue}// 4.如果是对象类型, 才需要创建对象if (map.get(originValue)) {return map.get(originValue)}const newObj = Array.isArray(originValue) ? []: {}map.set(originValue, newObj)// 遍历普通的keyfor (const key in originValue) {newObj[key] = deepCopy(originValue[key], map);}// 单独遍历symbolconst symbolKeys = Object.getOwnPropertySymbols(originValue)for (const symbolKey of symbolKeys) {newObj[Symbol(symbolKey.description)] = deepCopy(originValue[symbolKey], map)}return newObj
}

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

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

相关文章

Java抽象类和接口的学习了解

目录 1. 抽象类 1.1 抽象类概念 1.2例子 1.3 抽象类语法 1.被 abstract 修饰的类--抽象类 2.抽象类中被 abstract 修饰的方法--抽象方法,该方法不用给出具体的实现体 3.当一个类中含有抽象方法时,该类必须要abstract修饰 4.抽象类也是类&#xff…

删除有序数组中的重复项(同向指针(快慢指针))

题目: 算法分析: 快慢指针从0出发若快慢指针不相同,快指针替换慢指针(即慢指针后一位)快指针每次都会增加题目求不重复的元素个数(slow 为对应元素索引,故个数为slow1) 算法图解…

算法练习题27——疫情下的电影院(模拟)

其实思路还好 就是输入有点难搞 Java import java.util.ArrayList; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);String input scanner.nextLine();// 去掉输入字符串的方括号if (input.…

html+css+js网页设计 旅游 大理旅游7个页面

htmlcssjs网页设计 旅游 大理旅游7个页面 网页作品代码简单,可使用任意HTML辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作)。 获取源码 1&#…

爬虫逆向学习(六):补环境过某数四代

声明:本篇文章内容是整理并分享在学习网上各位大佬的优秀知识后的实战与踩坑记录 引用博客: https://blog.csdn.net/shayuchaor/article/details/103629294 https://blog.csdn.net/qq_36291294/article/details/128600583 https://blog.csdn.net/weixin_…

浅谈Spring Cloud:认识微服务

SpringCloud就是分布式微服务架构的一站式解决方案,是微服务架构落地的多种技术的集合。 目录 微服务远程调用 Eureka注册中心 搭建Eureka Server 注册组件 服务拉取 当各种各样的服务越来越多,拆分的也越来越细,此时就会出现一个服务集…

【Vue】2

1 Vue 生命周期 Vue生命周期:一个 Vue 实例从 创建 到 销毁 的整个过程 创建(create)阶段:组件实例化时,初始化数据、事件、计算属性等挂载(mount)阶段:将模板渲染并挂载到 DOM 上更新(update)阶段:当数据发生变化时…

Python基础语法(3)上

函数 函数是什么 编程中的函数和数学中的函数有一定的相似之处. 数学上的函数,比如 y sin x,x 取不同的值,y 就会得到不同的结果 编程中的函数是一段可以被重复使用的代码片段 代码示例:求数列的和,不使用函数 …

【计算机网络 - 基础问题】每日 3 题(六)

✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/fYaBd 📚专栏简介:在这个专栏中,我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏&…

C++:STL详解(一)string类的基本介绍与使用方式

✨ Blog’s 主页: 白乐天_ξ( ✿>◡❛) 🌈 个人Motto:实践是检验真理的唯一标准!!!敲代码需要勤快点!!!! 💫 欢迎来到我的学习笔记&#xff0…

docker-01 创建一个自己的镜像并运行容器

docker-01 创建一个自己的镜像并运行容器 前言 我们都知道使用Docker的镜像可以快速创建和部署应用,大大的节约了部署的时间。并且Docker 的镜像提供了除内核外完整的运行时环境,确保代码的环境一致性,从而不会在出现这段代码在我机器上没问…

用于遥感深度学习的7种高光谱遥感图像和标签

数据介绍 此数据集来自于GIC(GRUPO INTELIGENCIA COMPUTACIONAL )官网 直达链接,采用MATLAB存储为矩阵形式,数据集后缀为.mat形式。每一个数据分为原始图像数据和标签数据,标签对应码请参考官网。注:此数据为公开数据&#xff0c…

国产视频转换HDMI1.4转单/双MIPI DSI/CSI LT6911C芯片方案,带音频输出,QFN64封装 Lontium

LT6911C:HDMI 1.4 TO MIPI DSI/CSI 芯片简介: LT6911C是一款高性能的HDMI1.4转换器MIPI DSI/CSI芯片用于VR/智能手机/显示应用。对于MIPI DSI/CSI输出,LT6911C功能可配置单端口或双端口MIPIDSI/CSI 1高速时钟通道和1~4个高速数据通道最大1.5Gb/s/lane&am…

网络工程师学习笔记——网络互连与互联网

互联网的定义 由多个网络相互连接组成更大的网络称为互联网 常见的网络设备(是网络拓扑结构和网络的基础) 物理层 中继器(是将传输的信号进行放大,延长传输的距离),集线器也是这样,但是有更多…

如何获取MySQL数据表的列信息

在数据库管理中,了解表的结构是至关重要的。在MySQL中,我们可以通过几种方式来获取数据表的列信息。这不仅可以帮助我们更好地理解表的结构,还可以在编写查询时提供便利。以下是三种常用的方法来获取MySQL数据表的列信息。 使用 SHOW COLUMN…

C++速通LeetCode简单第10题-翻转二叉树

递归法: class Solution { public:TreeNode* invertTree(TreeNode* root) {if (root nullptr) {return nullptr;}TreeNode* left invertTree(root->left);TreeNode* right invertTree(root->right);root->left right;root->right left;return roo…

AtCoder ABC369 A-D题解

比赛链接:ABC369 省流&#xff1a;A<B<D<C&#xff08;题解是按照该顺序写的&#xff09; Problem A: #include <bist/stdc.h> using namespace std; int main(){int A,B;cin>>A>>B;if(AB)cout<<1<<endl;else if(abs(A-B)%20)cout&l…

一个软件分发和下载的网站源码,带多套模板

PHP游戏应用市场APP软件下载平台网站源码手机版 可自行打包APP&#xff0c;带下载统计&#xff0c;带多套模板&#xff0c;带图文教程 代码下载&#xff1a;百度网盘

饿了么基于Flink+Paimon+StarRocks的实时湖仓探索

摘要&#xff1a;本文整理自饿了么大数据架构师、Apache Flink Contributor 王沛斌老师在8月3日 Streaming Lakehouse Meetup Online&#xff08;Paimon x StarRocks&#xff0c;共话实时湖仓架构&#xff09;上的分享。主要分为以下三个内容&#xff1a; 饿了么实时数仓演进之…

C语言-整数和浮点数在内存中的存储-详解-上

C语言-整数和浮点数在内存中的存储-详解-上 1.前言2.整数2.1无符号整数2.2原码、反码、补码符号位最大值转换过程补码的意义简化算术运算易于转换方便溢出处理 1.前言 在C语言的使用中&#xff0c;需要时刻关注数据的类型&#xff0c;不同类型交替使用可能会发生错误&#xff…