当前位置: 首页 > 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最古老的形式。管道只能作用于有血缘关系的进程之间&…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例&#xff0c;Webpack.config.js它可能的配置和含义如下&#xff1a; 前言 Module Federation 的Webpack.config.js核心配置包括&#xff1a; name filename&#xff08;定义应用标识&#xff09; remotes&#xff08;引用远程模块&#xff0…...