在那小小的梦的暖阁,我为你收藏起整个季节的烟雨。

——洛夫《灵河》

本文为读 lodash 源码的第四篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash

gitbook也会同步仓库的更新,gitbook地址:pocket-lodash

作用与用法

Hash 顾名思义,就是要有一个离散的序列,根据 key 来储取数据。而在 javascript 中,最适合的无疑是对象了。

Hash 在 lodash 的 .internal 文件夹中,作为内部文件来使用。lodash 会根据不同的数据类型选择不同的缓存方式,Hash 便是其中的一种方式,这种方式只能缓存 key 的类型符合对象键要求的数据。

Hash 只接收一个二维数组作为参数,调用方式如下:

new Hash([['tes1',1],['test2',2],['test3',3]])

其中子项中的第一项会作为 key ,第二项是需要缓存的值。

Hash 实例化的结果如下:

{
size: 3,
__data__: {
test1: 1,
test2: 2,
test3: 3
}
}

缓存的数量储存在 __data__ 的对象中。

Hash与Map

后面将会讲到,除了使用 Hash 方式缓存数据外,还会用到 Map,lodash 在设计 Hash 的数据管理接口时,也与 Map 的接口一致,但是不会包含 Map 的遍历方法。

先来看看这些接口都有那些:

源码

const HASH_UNDEFINED = '__lodash_hash_undefined__'

class Hash {
constructor(entries) {
let index = -1
const length = entries == null ? 0 : entries.length this.clear()
while (++index < length) {
const entry = entries[index]
this.set(entry[0], entry[1])
}
}
clear() {
this.__data__ = Object.create(null)
this.size = 0
}
delete(key) {
const result = this.has(key) && delete this.__data__[key]
this.size -= result ? 1 : 0
return result
}
get(key) {
const data = this.__data__
const result = data[key]
return result === HASH_UNDEFINED ? undefined : result
}
has(key) {
const data = this.__data__
return data[key] !== undefined
}
set(key, value) {
const data = this.__data__
this.size += this.has(key) ? 0 : 1
data[key] = value === undefined ? HASH_UNDEFINED : value
return this
}
} export default Hash

constructor

constructor(entries) {
let index = -1
const length = entries == null ? 0 : entries.length this.clear()
while (++index < length) {
const entry = entries[index]
this.set(entry[0], entry[1])
}
}

constructor 中并没有看到初始化 __data__ 属性和 size 属性,这个其实在 clear 方法中初始化了,后面会解释。

接着遍历传入的二维数组,调用 set 方法,初始化缓存的值。将子项的第一项作为 key ,第二项为缓存的值。

clear

clear() {
this.__data__ = Object.create(null)
this.size = 0
}

clear 的作用是清空缓存,因此需要将 size 重置为 0

将缓存的数据 __data__ 设置为空对象。

这里并没有用 this.__data__ = {} 置空,而是调用了 Object.create 方法,并且将 null 作为参数。我们都知道, Object.create 的第一个参数为创建对象的原型对象,传入 null 的时候,返回的就是一个真空对象,即没有原型的对象,因此不会有原型属性的干扰,用来做缓存对象十分适合。

has

has(key) {
const data = this.__data__
return data[key] !== undefined
}

has 用来判断是否已经有缓存数据,如果缓存数据已经存在,则返回 true

判断也十分简单,只需要判断取出来的值是否为 undefined 即可。

这个判断有一个坑,后面会讲到。

set

set(key, value) {
const data = this.__data__
this.size += this.has(key) ? 0 : 1
data[key] = value === undefined ? HASH_UNDEFINED : value
return this
}

set 用来增加或者更新需要缓存的值。set 的时候需要同时维护 size 和在缓存的值。

首先调用 has 方法,判断对应的 key 是否已经被缓存过,如果已经缓存过,则 size 保持不变,否则 size1

缓存值其实就是设置缓存对象 this.__data__ 对应 key 属性的值。

has 中说到用 data[key] !== undefined 有一个坑,因为要缓存的值也可以是 undefined ,如果不做处理,肯定会导致判断错误。

lodash 的处理方式是将 undefined 的值转换成 HASH_UNDEFINED ,也即一开始便定义的 __lodash_hash_undefined__ 字符串来储存。

所以在缓存中,是用字符串 __lodash_hash_undefined__ 来替代 undefined 的。

set 在最后还将实例 this 返回,以支持链式操作。

get

get(key) {
const data = this.__data__
const result = data[key]
return result === HASH_UNDEFINED ? undefined : result
}

get 方法是从缓存中取值。

取值其实就是返回缓存对象中对应 key 的值即可。因为 undefined 在缓存中是以 __lodash_hash_undefined__ 来表示的,因此遇到值为 __lodash_hash_undefined__ 时,返回 undefined

其实这样还是有小小的问题的,如果需要缓存的值刚好是 __lodash_hash_undefined__,那取出来的值跟预设的就不一致了。但是这样情况应该很少出现吧。

delete

delete(key) {
const result = this.has(key) && delete this.__data__[key]
this.size -= result ? 1 : 0
return result
}

delete 方法用来删除指定 key 的缓存。成功删除返回 true, 否则返回 false。 删除操作同样需要维护 size 属性和缓存值。

首先调用 has 方法来判断缓存是否存在,如果存在,用 delete 操作符将 __data__ 中对应的属性删除。

delete 操作符在成功删除属性时会返回 true,如果成功删除,则需要将 size 减少 1

参考

  1. Set 和 Map 数据结构
  2. Object.create()

License

署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)

最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:

作者:对角另一面

lodash源码分析之Hash缓存的更多相关文章

  1. lodash源码分析之List缓存

    昨日我沿着河岸/漫步到/芦苇弯腰喝水的地方 顺便请烟囱/在天空为我写一封长长的信 潦是潦草了些/而我的心意/则明亮亦如你窗前的烛光/稍有暧昧之处/势所难免/因为风的缘故 --洛夫<因为风的缘故& ...

  2. lodash源码分析之缓存方式的选择

    每个人心里都有一团火,路过的人只看到烟. --<至爱梵高·星空之谜> 本文为读 lodash 源码的第八篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash gitb ...

  3. lodash源码分析之缓存使用方式的进一步封装

    在世界上所有的民族之中,支配着他们的喜怒选择的并不是天性,而是他们的观点. --卢梭<社会与契约论> 本文为读 lodash 源码的第九篇,后续文章会更新到这个仓库中,欢迎 star:po ...

  4. lodash源码分析之自减的两种形式

    这个世界需要一个特定的恶人,可以供人们指名道姓,千夫所指:"全都怪你". --村上春树<当我谈跑步时我谈些什么> 本文为读 lodash 源码的第六篇,后续文章会更新到 ...

  5. Solr4.8.0源码分析(19)之缓存机制(二)

    Solr4.8.0源码分析(19)之缓存机制(二) 前文<Solr4.8.0源码分析(18)之缓存机制(一)>介绍了Solr缓存的生命周期,重点介绍了Solr缓存的warn过程.本节将更深 ...

  6. lodash源码分析之数组的差集

    外部世界那些破旧与贫困的样子,可以使我内心世界得到平衡. --卡尔维诺<烟云> 本文为读 lodash 源码的第十七篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodas ...

  7. Solr4.8.0源码分析(18)之缓存机制(一)

    Solr4.8.0源码分析(18)之缓存机制(一) 前文在介绍commit的时候具体介绍了getSearcher()的实现,并提到了Solr的预热warn.那么本文开始将详细来学习下Solr的缓存机制 ...

  8. lodash源码分析之baseFindIndex中的运算符优先级

    我悟出权力本来就是不讲理的--蟑螂就是海米:也悟出要造反,内心必须强大到足以承受任何后果才行. --北岛<城门开> 本文为读 lodash 源码的第十篇,后续文章会更新到这个仓库中,欢迎 ...

  9. java-通过 HashMap、HashSet 的源码分析其 Hash 存储机制

    通过 HashMap.HashSet 的源码分析其 Hash 存储机制 集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并非真正的把 Java 对象放入数组中.仅仅是把对象的 ...

随机推荐

  1. sockt套接字编程

    一.Socket简介 Socket是进程通讯的一种方式,即调用这个网络库的一些API函数实现分布在不同主机的相关进程之间的数据交换. 几个定义: (1)IP地址:即依照TCP/IP协议分配给本地主机的 ...

  2. 通过ELK快速搭建一个你可能需要的集中化日志平台

    在项目初期的时候,大家都是赶着上线,一般来说对日志没有过多的考虑,当然日志量也不大,所以用log4net就够了,随着应用的越来越多,日志散 落在各个服务器的logs文件夹下,确实有点不大方便,这个时候 ...

  3. android stdio 快捷键

    俗话说工欲善其事必先利其器,先熟悉快捷键是很有必要的                 IDE   按键                                                 ...

  4. smali语法(二)

    一.smali的包中信息 .class public Lcom/aaaaa; .super Lcom/bbbbb; .source "ccccc.java" 1.它是com.aaa ...

  5. 单节点下使用docker部署consul

    部署consul 目前Consul使用的版本是: v1.0.1 本教程适用于刚刚开始学习consul并简单使用consul的同学,可以在短时间内了解conusl,配合官方文档https://www.c ...

  6. JavaSE学习入门

    Java基础: 1.安装JDK1.7(JDK 包括JRE,Java工具包,Java的类库) 2.编写Hello,world 程序 public class Hello{ public static v ...

  7. Cesium基础使用介绍

    前言 最近折腾了一下三维地球,本文简单为大家介绍一款开源的三维地球软件--Cesium,以及如何快速上手Cesium.当然三维地球重要的肯定不是数据显示,这只是数据可视化的一小部分,重要的应该是背后的 ...

  8. 读懂源码:一步一步实现一个 Vue

    源码阅读:究竟怎样才算是读懂了? 市面上有很多源码分析的文章,就我看到的而言,基本的套路就是梳理流程,讲一讲每个模块的功能,整篇文章有一大半都是直接挂源码.我不禁怀疑,作者真的看懂了吗?为什么我看完后 ...

  9. Azure 基础:使用 Traffic Manager 分流用户请求

    为了减少 web 服务器的宕机时间,同时也提高服务器的响应性能,我们往往部署多个站点并通过负载均衡来对外提供服务.Azure 提供的 Traffic Manager 服务属于负载均衡的一种,特点是工作 ...

  10. 蓝桥杯 剪邮票 全排列+DFS

    剪邮票 如[图1.jpg], 有12张连在一起的12生肖的邮票. 现在你要从中剪下5张来,要求必须是连着的. (仅仅连接一个角不算相连) 比如,[图2.jpg],[图3.jpg]中,粉红色所示部分就是 ...