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

彻底理解浅拷贝和深拷贝

目录

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

浅拷贝

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

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

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

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

实现

  • 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
}

相关文章:

彻底理解浅拷贝和深拷贝

目录 浅拷贝实现 深拷贝实现自己手写 浅拷贝 浅拷贝是指创建一个新对象,这个对象具有原对象属性的精确副本 基本数据类型(如字符串、数字等),在浅拷贝过程中它们是通过值传递的,而不是引用传递,修改值并不…...

Spring4-IoC2-基于注解管理bean

目录 开启组件扫描 使用注解定义bean Autowired注入 场景一:属性注入 场景二:set注入 场景三:构造方法注入 场景四:形参注入 场景五:只有一个构造函数,无注解 场景六:Autowired和Quali…...

AI基础 L22 Uncertainty over Time I 时间的不确定性

Time and Uncertainty 1 Time and Uncertainty States and Observations • discrete-time models: we view the world as a series of snapshots or time slices • the time interval ∆ between slices, we assume to be the same for every interval • Xt: denotes the se…...

中小型企业网络构建

1 什么是 VLAN? VLAN,指的是虚拟局域网,是一种 2 层技术。可以在交换机上实现广播域的隔离。从而可以减小 数据广播风暴对交换网络的影响,降低了网络管理难度,同时可以实现网络规模的灵活扩展。 2 Trunk 链路与 Acces…...

PXE服务

一.PXE服务的功能介绍 1.无盘启动:PXE允许计算机在没有本地存储设备的情况下启动操作系统。这对于构建无盘工作站非常有用,因为计算机可以直接从网络加载操作系统和其他应用程序1。 2.远程安装操作系统:PXE技术可以用于远程安装操作系统&…...

Docker技术深度解析与实践应用

Docker技术深度解析与实践应用 引言 在现代软件开发与部署的浪潮中,Docker作为一种轻量级的容器化技术,凭借其高效、一致和灵活的特性,逐渐成为云原生应用开发和部署的基石。本文将深入探讨Docker的核心概念、技术原理、实践应用&#xff0…...

链动321模式小程序开发源码

链动31模式概述 链动31模式是一种基于技术的新型商业模式,它通过激励用户分享和推广,实现用户、企业和平台的共赢。该模式通常涉及商品展示、积分系统、分享推广和排行榜等功能,旨在通过用户之间的社交裂变来扩大销售和品牌影响力。如何开发这…...

java开发中间件学习记录(持续更新中~)

1 Redis 2JVM 3 java基础底层 4Mysql 5 spring 6 微服务 7.......(持续更新) One:Redis篇 1:Redis 1.穿透 1.1缓存穿透 1.1.1布隆过滤器 1.2缓存击穿 2:击穿 1.3:缓存雪崩 1.4:双写一致 1.5.持久化(RDB,AOF) 1.6…...

(批处理)无限弹窗cmd

代码部分 echo off echo 好了,可以退出了 pause>nul echo 再点就要无限弹窗了! pause >nul echo 你还点? pause >nul echo 再给你最后一次机会,别点了,再点准备重启 pause >nul echo 点击任意键变身奥特曼…...

解决ubuntu 24.04 ibus出现卡死、高延迟问题

问题描述 ubuntu中使用ibus经常会出现卡死、高延迟的问题,网上找了一些解决方法就手动输入命令是重启。但是键盘卡死了没法输入,不能很有效的解决问题。 解决思路 通过一个bash脚本监测ibus进程,当出现进程卡死的时候自动重启。 bash代码…...

减少脏页标记技术中处理时间的方法

减少脏页标记技术中处理时间的方法 一、引言 在数据库系统中,脏页标记技术对于确保数据的一致性和持久性至关重要。然而,脏页标记过程可能会消耗一定的处理时间,影响数据库的性能。因此,寻找有效的方法来减少脏页标记技术中的处理时间具有重要意义。 二、优化数据结构 …...

828华为云征文 | 华为云Flexusx与Docker技术融合,打造个性化WizNote服务

前言 华为云Flexus X实例携手Docker技术,创新融合打造高效个性化WizNote服务。华为云Flexus X实例的柔性算力与Docker的容器化优势相结合,实现资源灵活配置与性能优化,助力企业轻松构建稳定、高效的云端笔记平台。828华为云企业上云节特惠来袭…...

JavaScript事件处理和常用对象

文章目录 前言一、事件处理程序 1.JavaScript 常用事件2.事件处理程序的调用二、常用对象 1.Window 对象2.String 对象3.Date 对象总结 前言 JavaScript 语言是事件驱动型的。这意味着,该门语言可以通过事件触发来调用某一函数或者一段代码。该文还简单介绍了Window…...

Qt基础类05-尺寸类QSize

Qt基础类05-尺寸类QSize 摘要基本信息写在前面重要成员函数举例7个QSize QSize::boundedTo(const QSize &otherSize) constQSize QSize::expandedTo(const QSize &otherSize) constbool QSize::isEmpty() constbool QSize::isNull() constbool QSize::isValid() constQ…...

Vue 2中的this指向详解

在JavaScript中,this的指向是许多开发者经常遇到的问题,尤其是在使用Vue这样的框架时。在Vue 2中,理解this的指向对于正确地访问组件的数据和方法至关重要。 1. this在Vue组件中的指向 在Vue组件的选项中,this通常指向当前组件实…...

长业务事务的离线并发问题

事务指代一组操作同时成功或同时失败,事务可分为两类: 系统事务:即关系数据库事务,一次数据库连接中由start transaction或begin开启,commit表示提交,rollback表示回滚;业务事务:完…...

黑马程序员Java笔记整理(day01)

1.windowsR进入运行,输入cmd 2.环境变量 3.编写java第一步 4.使用idea 5.注释 6.字面量 7.变量 8.二进制 9.数据类型 10.关键词与标识符...

VMware Tools系列一:安装VMware Tools的作用

最近笔者安装了VMware Workstation pro 17,同时在VMware中安装了华为的Open Euler服务器,由于虚拟机Open Euler经常需要与宿主机win10交换数据,很不方便,而安装VMware Workstation pro 17并没有自动安装了VMware Tools &#xff0…...

使用大语言模型(LLM)修正小段乱码(Mojibake)为正常文本

Python方案 在上一篇文章ftfy:修正小段乱码(Mojibake)为正常文本的Python库中,我介绍了ftfy这个库。但随着持续的开发,我发现它仍然有一些解决不了的转换。如下: >>> ftfy.fix_text(‡›‘…...

C++ 访问限定符

个人主页:Jason_from_China-CSDN博客 所属栏目:C系统性学习_Jason_from_China的博客-CSDN博客 所属栏目:C知识点的补充_Jason_from_China的博客-CSDN博客 概念概述 C一种实现封装的方式,用类将对象的属性与方法结合在一块&#xf…...

几种mfc140u.dll常见错误情况,以及mfc140u.dll文件修复的方法

如果你遇到与mfc140u.dll 文件相关的错误,这通常指的是该mfc140u.dll文件可能丢失、损坏或与您的应用程序不兼容。详细分析关于mfc140u.dll文件错误会对系统有什么影响,mfc140u.dll文件处于什么样的位置?以下是几种常见的错误情况及其修复方法…...

[产品管理-21]:NPDP新产品开发 - 19 - 产品设计与开发工具 - 详细设计与规格定义

目录 前言: 一、详细设计与规格定义概述 1、产品详细设计 2、规格定义 3、详细设计与规格定义的关系 4、实际应用中的注意事项 二、详细设计与规格定义主要工具 2.1 质量功能展开QFD - 需求跟踪矩阵 1、QFD的基本原理 2、QFD的实施步骤 3、QFD的优势与应…...

powershell@文件大小排序@文件夹磁盘占用分析

文章目录 abstractpowershell对于列出子目录大小的支持状况按文件大小排序指定目录下所有文件 扩展的专用函数介绍统计目录或文件的大小👺对指定目录以友好的格式列出所有文件和子目录的大小函数灵活性说明👺 综合用例其他相关软件易用系列 abstract 在…...

详解c++多态---上

virtual关键字 1.可以修饰原函数&#xff0c;为了完成虚函数的重写&#xff0c;满足多态的条件之一。 class Person { public:virtual void BuyTicket() { cout << "买票-全价" << endl; } };class Student : public Person { public:virtual void Buy…...

emWin5的图片半透明之旅

文章目录 目标过程直接使用png (失败了)通过 BmpCvt.exe 转换一下&#xff08;成功了&#xff09;通过bmp转 &#xff08;半成功吧&#xff09; 补充工程结构整理 目标 显示半透明效果&#xff0c;类似png那种&#xff0c;能透过去&#xff0c;看到背景。 过程 直接使用png …...

【RabbitMQ 项目】项目概述

项目概述 一.角色划分二.服务器模块概述1.本地模块2.网络模块3.服务器模块 三.模块详细划分1.服务端2.客户端 一.角色划分 该项目的模型是一个跨主机的生产消费模型&#xff0c;有三种角色&#xff1a;生产者&#xff0c;消费者&#xff0c;中间人。对应就要实现三个大模块&…...

【VSCode】VSCode Background 背景插件辅助窗口程序

前排贴上Github项目链接 GitHub窗口项目链接 这是一个基于VSCode上由shalldie上传的background扩展制作的windows窗口程序。 该程序旨在通过窗口程序尽可能的完善该扩展原有的功能。 background - shalldie 的最大优势是我目前仅在其扩展上发现了UseFront的选项&#xff0c;这…...

gdb 前端:kdbg 安装使用

文章目录 1. 前言2. kdbg 安装使用2.1 安装 kdbg2.2 使用 kdbg 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. kdbg 安装使用 2.1 安装 kdbg kdbg 是 gdb 的图形化界面的前端&#xff0c;在 …...

UDP_SOCKET编程实现

文章目录 socket编程接口认识struct sockaddr类 编写一个server服务Client代码查看启动结果代码修正1.获取内核分配给客户端的信息2.修正不匹配ip不能访问的问题 不同机器之间的通信利用xftp将udp_client传给wsl的ubuntu机器进行演示现在模拟在windows下的udp_client代码: 对方…...

【网络】高级IO——阻塞IO和非阻塞IO的实现

目录 一.文件描述符的默认行为——阻塞IO 二.非阻塞IO 2.1.在打开文件或创建套接字时设置非阻塞模式&#xff1a; 2.2.在使用网络I/O接口时请求非阻塞行为&#xff1a; 2.3.fcntl函数 一.文件描述符的默认行为——阻塞IO 在Linux系统中&#xff0c;无论是通过open系统调用…...