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

lodash之cloneDeep()源码阅读笔记

lodash之cloneDeep()源码阅读笔记

基本上都在写业务代码,没有机会写库,还是想了解一下lodash的库源码是怎么样的,平时用的最多的就是cloneDeep()方法了,终于有空详细看看其中的源码。

本文基于lodash@5.0.0版本的源码进行阅读。

/cloneDeep.js

cloneDeep入口函数

import baseClone from './.internal/baseClone.js';const CLONE_DEEP_FLAG = 1
const CLONE_SYMBOLS_FLAG = 4
function cloneDeep(value) {return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG)
}export default cloneDeep

调用了一个内部的方法,传入的参数中有一个CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG,查了一下mdn文档操作符|,是一个按位或的语法。

const a = 1; // 00000000000000000000000000000001
const b = 4; // 00000000000000000000000000000100console.log(a | b); // 00000000000000000000000000000101
// Expected output: 5

给我看懵了,注释是说用于合成用于克隆的位掩码,也不知道为啥这样传,继续看baseClone的代码。

/.internal/baseClone.js

lodash中很多代码都抽象出来了,先找到baseClone方法

位掩码传参

const CLONE_DEEP_FLAG = 1
const CLONE_FLAT_FLAG = 2
const CLONE_SYMBOLS_FLAG = 4
/**
* @param {number} bitmask 位掩码标志.
*  1 - 深拷贝
*  2 - 扁平化继承属性
*  4 - 克隆 symbols
*/
function baseClone(value, bitmask, customizer, key, object, stack) {let resultconst isDeep = bitmask & CLONE_DEEP_FLAGconst isFlat = bitmask & CLONE_FLAT_FLAGconst isFull = bitmask & CLONE_SYMBOLS_FLAG// ...
}

这一部分结合bitmask参数的注释,算是看明白为啥用按位或这样的方式进行传参了。这里想到linux的权限的方式,就可以只传一个参数,实际上是可以判断三个boolean类型的参数,还能加更多,秒啊。

非对象的判断,原始数据类型直接返回

// baseClone.js
if (!isObject(value)) {return value
}
// /isObject.js
function isObject(value) {const type = typeof valuereturn value != null && (type === 'object' || type === 'function')
}export default isObject

这里判断了传入被clone的值不是一个对象或函数,则直接返回,对于原始数据类型的话,是可以直接clone的。

判断是正则RegExp#exec方法执行结果

接下来采用const isArr = Array.isArray(value)判断了值是否是数组,然后初始化了一个同等长度的数组,其中又有一个看不懂的判断

const hasOwnProperty = Object.prototype.hasOwnProperty
if (length && typeof array[0] === 'string' && hasOwnProperty.call(array, 'index')) {result.index = array.indexresult.input = array.input}

Object.prototype.hasOwnProperty.call(array, ‘index’)是可以判断array中是否有index属性,这数组中有index属性,仔细看注释,添加由RegExp#exec指定的属性,想到这个可能是RegExp中的exec方法执行的结果,RegExp.prototype.exec(),发现果然是有元素0是字符串,并且含有index和input两个元素。

// 例如
RegExp('foo*', 'g').exec('table football, foosball')
// 控制台结果
// 0: "foo"
// groups: undefined
// index: 6
// input: "table football, foosball"
// length: 1
// [[Prototype]]: Array(0)

clone buffer

对于深拷贝的buffer,平时的业务场景,基本上不用到buffer,发现处理是使用了buffer的一个ArrayBuffer.prototype.slice()方法,返回一个新的 ArrayBuffer。

initCloneObject 初始化克隆对象

对于扁平的和函数,是直接使用{},对于其他的一些从原型链继承的自定义对象,使用了Object.create(Object.getPrototypeOf(object))来初始化对象。

Boolean和Date使用构造器创建对象

function initCloneByTag(object, tag, isDeep) {const Ctor = object.constructorswitch (tag) {// ...case boolTag:case dateTag:return new Ctor(+object)// ...case mapTag:return new Ctorcase setTag:return new Ctor}
}

对于Boolean对象和Date对象直接采用了Object.prototype.contructor取对象的构造器来进行对象创建,其中用到了一个前+的计算,把false、true,以及Date对象转成了数字。

set和map分别实例化一个新的对象

Stack对象

看/.internal/Stack.js中实现了栈数据类型,包含data和size属性,以及基础的清空、删除、查找是否存在、新增、根据索引获取值,实现了一个最大长度为200的栈。

lodash倒是没有实现判断空的方法,而是直接在ListCache对象的delete方法中判断了data的长度是否为零,为零的话栈中是没有元素可以删除的,返回了false。

栈是先入后出的特殊链表,之前学数据结构的时候已经实现过js的stack结构。

Set和Map类型的数据,遍历递归并压入栈

stack || (stack = new Stack)const stacked = stack.get(value)if (stacked) {return stacked}stack.set(value, result)if (tag == mapTag) {value.forEach((subValue, key) => {result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack))})return result}if (tag == setTag) {value.forEach((subValue) => {result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack))})return result}

这里对于Map和Set中的下一级对象,采用了递归调用的方式进行赋值操作,并且把实例化的栈的信息传递进去,看注释采用Stack是检查循环引用并返回其对应的克隆。

总结

整体的实现思路大概是:

首先判断要复制的对象是否为原始类型,如果是原始类型则直接返回该值。如果是引用类型(如对象或数组),则进行深度复制。

创建一个新的空对象或数组作为目标对象。

遍历要复制的对象的属性或元素,对每一个属性或元素进行递归复制,直到所有嵌套的对象或数组都被复制为止。

使用递归复制的方式,将源对象或数组的属性或元素复制到目标对象或数组中。如果属性或元素本身是一个引用类型,则递归调用 baseClone 进行深度复制。

返回复制完成的目标对象或数组。

相关文章:

lodash之cloneDeep()源码阅读笔记

lodash之cloneDeep()源码阅读笔记 基本上都在写业务代码,没有机会写库,还是想了解一下lodash的库源码是怎么样的,平时用的最多的就是cloneDeep()方法了,终于有空详细看看其中的源码。 本文基于lodash5.0.0版本的源码进行阅读。 /…...

算法模版,今天开始背

二分查找算法 int left_bound(int[] nums, int target) {int left 0, right nums.length - 1;// 搜索区间为 [left, right]while (left < right) {int mid left (right - left) / 2;if (nums[mid] < target) {// 搜索区间变为 [mid1, right]left mid 1;} else if …...

新的 Python URL 解析漏洞可能导致命令执行攻击

Python URL 解析函数中的一个高严重性安全漏洞已被披露&#xff0c;该漏洞可绕过 blocklist 实现的域或协议过滤方法&#xff0c;导致任意文件读取和命令执行。 CERT 协调中心&#xff08;CERT/CC&#xff09;在周五的一份公告中说&#xff1a;当整个 URL 都以空白字符开头时&…...

react项目做的h5页面加载缓慢优化(3s优化到0.6s)

打包到生产环境时去掉SOURCEMAP 禁用生成 Source Map 是一种权衡&#xff0c;可以根据项目的实际需求和优化目标来决定是否禁用。如果您对调试需求不是特别强烈&#xff0c;可以考虑在生产构建中禁用 Source Map 以获取更好的性能。但如果需要保留调试能力&#xff0c;可以在生…...

如何修复损坏的DOC和DOCX格式Word文件?

我们日常办公中&#xff0c;经常用到Word文档。但是有时会遇到word文件损坏、无法打开的情况。这时该怎么办&#xff1f;接着往下看&#xff0c;小编在这里就给大家带来最简单的Word文件修复方法&#xff01; 很多时候DOC和DOCX Word文件会无缘无故的损坏无法打开&#xff0c;一…...

UI设计师个人工作感悟5篇

UI设计师个人工作感悟一 工作一年了&#xff0c;结合我自身谈谈UI设计的重要性。现在主流的论坛建站程序有两种 Phpwind 和Discuz(Phpwind被阿里巴巴收购 Discuz被腾讯收购这两个论坛程序都是开源免费的)&#xff0c;利用这两种程序我都分别建立过论坛&#xff0c;我第一次用的…...

Java堆、栈、内存的知识

在JAVA中&#xff0c;有六个不同的地方可以存储数据&#xff1a; 1.寄存器&#xff1a;最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 栈&#xff1a;存放基本类型的变量数据和对象的引用&#xff0c;但对象本身不存放在栈中&#xff0c;而是存放在堆&…...

tp6 RabbitMQ

1、composer 安装 AMQP 扩展 composer require php-amqplib/php-amqplib 2、RabbitMQ 配置 在 config 目录下创建 rabbitmq.php 文件 <?php return [host>,port>5672,user>,password>,vhost>,exchange_name > ,queue_name > ,route_key > ,cons…...

java Spring Boot yml多环境拆分文件管理优化

上文 java Spring Boot yml多环境配置 我们讲了多环境开发 但这种东西都放在一起 还是非常容易暴露信息的 并且对维护来讲 也不是非常的友好 这里 我们在resources下创建三个文件 分别叫 application-pro.yml application-dev.yml application-test.yml 我们直接将三个环境 转…...

【设计模式——学习笔记】23种设计模式——状态模式State(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录 案例引入介绍基本介绍登场角色应用场景 案例实现案例一类图实现 案例二&#xff1a;借贷平台源码剖析传统方式实现分析状态修改流程类图实现 案例三&#xff1a;金库警报系统系统的运行逻辑伪代码传统实现方式使用状态模式 类图实现分析问题问题一问题二 总结文章说明…...

【LeetCode每日一题】——41.缺失的第一个正数

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 哈希表 二【题目难度】 困难 三【题目编号】 41.缺失的第一个正数 四【题目描述】 给你一个…...

typedef函数代码段解释以及部分Windows下的系统函数

文章目录 1、typedef int (WINAPI* LPSDOLInitialize)(const SDOLAppInfo* pAppInfo)2、typedef int (WINAPI* LPSDOLGetModule)(REFIID riid, void** intf)3、typedef int (WINAPI* LPSDOLTerminal)();4、GetProcAddress运行时获取一个动态链接库&#xff08;DLL&#xff09;中…...

Typora常用手册

常用快捷键 加粗&#xff1a; Ctrl B 标题&#xff1a; Ctrl H 插入链接&#xff1a; Ctrl K 插入代码&#xff1a; Ctrl Shift C – 无法执行 行内代码&#xff1a; Ctrl Shift K 插入图片&#xff1a; Ctrl Shift I 无序列表&#xff1a;Ctrl Shift L – 无法执行…...

互联网发展历程:从网线不够长到中继器的引入

互联网&#xff0c;这个如今贯穿我们生活的无所不在的网络&#xff0c;其发展历程充满了无数的创新和变革。有一项看似不太起眼的技术却在互联网的发展中发挥着至关重要的作用&#xff0c;那就是中继器。本文将带您深入了解互联网的发展历程&#xff0c;探讨在网线不够长的情况…...

【Java】异常处理 之 使用SLF4J 和 Logback

使用SLF4J和Logback 前面介绍了Commons Logging 和Log4j 这一对好基友&#xff0c;它们一个负责充当日志 API&#xff0c;一个负责实现日志底层&#xff0c;搭配使用非常便于开发。 有的童鞋可能还听说过SLF4J和Logback。这两个东东看上去也像日志&#xff0c;它们又是啥&…...

C++11并发与多线程笔记 (1)

C11并发与多线程笔记&#xff08;1&#xff09; 1、并发、进程、线程的基本概念和综述1.1 并发1.2 可执行程序1.3 进程1.4 线程1.5 学习心得 2、并发的实现方法2.1 多进程并发2.2 多线程并发 3、C11新标准线程库 1、并发、进程、线程的基本概念和综述 1.1 并发 指在一个时间段…...

07_Hudi案例实战、Flink CDC 实时数据采集、Presto、FineBI 报表可视化等

7.第七章 Hudi案例实战 7.1 案例架构 7.2 业务数据 7.2.1 客户信息表 7.2.2 客户意向表 7.2.3 客户线索表 7.2.4 线索申诉表 7.2.5 客户访问咨询记录表 7.3 Flink CDC 实时数据采集 7.3.1 开启MySQL binlog 7.3.2 环境准备 7.3.3 实时采集数据 7.3.3.1 客户信息表 7.3.3.2 客户…...

ceph相关概念和部署

Ceph 可用于向云提供 Ceph 对象存储 平台和 Ceph 可用于提供 Ceph 块设备服务 到云平台。Ceph 可用于部署 Ceph 文件 系统。所有 Ceph 存储集群部署都从设置 每个 Ceph 节点&#xff0c;然后设置网络。 Ceph 存储集群需要满足以下条件&#xff1a;至少一个 Ceph 监控器&#x…...

Android Jetpack Compose 中的分页与缓存展示

Android Jetpack Compose 中的分页与缓存展示 在几乎任何类型的移动项目中&#xff0c;移动开发人员在某个时候都会处理分页数据。如果数据列表太大&#xff0c;无法一次从服务器检索完毕&#xff0c;这就是必需的。因此&#xff0c;我们的后端同事为我们提供了一个端点&#…...

无名管道 / 有名管道(FIFO)

根据上节所讲就可以了解到&#xff1a;管道其实就是实现进程间通讯IPC中的一种类型方法 基本概念&#xff08;无名管道&#xff09; 管道是一种最基本的IPC机制&#xff0c;通常指无名管道&#xff0c;也是UNIX系统IPC最古老的形式。管道只能作用于有血缘关系的进程之间&…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...