lodash源码分析之Hash缓存
在那小小的梦的暖阁,我为你收藏起整个季节的烟雨。
——洛夫《灵河》
本文为读 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
保持不变,否则 size
加 1
。
缓存值其实就是设置缓存对象 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
。
参考
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:
作者:对角另一面
lodash源码分析之Hash缓存的更多相关文章
- lodash源码分析之List缓存
昨日我沿着河岸/漫步到/芦苇弯腰喝水的地方 顺便请烟囱/在天空为我写一封长长的信 潦是潦草了些/而我的心意/则明亮亦如你窗前的烛光/稍有暧昧之处/势所难免/因为风的缘故 --洛夫<因为风的缘故& ...
- lodash源码分析之缓存方式的选择
每个人心里都有一团火,路过的人只看到烟. --<至爱梵高·星空之谜> 本文为读 lodash 源码的第八篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash gitb ...
- lodash源码分析之缓存使用方式的进一步封装
在世界上所有的民族之中,支配着他们的喜怒选择的并不是天性,而是他们的观点. --卢梭<社会与契约论> 本文为读 lodash 源码的第九篇,后续文章会更新到这个仓库中,欢迎 star:po ...
- lodash源码分析之自减的两种形式
这个世界需要一个特定的恶人,可以供人们指名道姓,千夫所指:"全都怪你". --村上春树<当我谈跑步时我谈些什么> 本文为读 lodash 源码的第六篇,后续文章会更新到 ...
- Solr4.8.0源码分析(19)之缓存机制(二)
Solr4.8.0源码分析(19)之缓存机制(二) 前文<Solr4.8.0源码分析(18)之缓存机制(一)>介绍了Solr缓存的生命周期,重点介绍了Solr缓存的warn过程.本节将更深 ...
- lodash源码分析之数组的差集
外部世界那些破旧与贫困的样子,可以使我内心世界得到平衡. --卡尔维诺<烟云> 本文为读 lodash 源码的第十七篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodas ...
- Solr4.8.0源码分析(18)之缓存机制(一)
Solr4.8.0源码分析(18)之缓存机制(一) 前文在介绍commit的时候具体介绍了getSearcher()的实现,并提到了Solr的预热warn.那么本文开始将详细来学习下Solr的缓存机制 ...
- lodash源码分析之baseFindIndex中的运算符优先级
我悟出权力本来就是不讲理的--蟑螂就是海米:也悟出要造反,内心必须强大到足以承受任何后果才行. --北岛<城门开> 本文为读 lodash 源码的第十篇,后续文章会更新到这个仓库中,欢迎 ...
- java-通过 HashMap、HashSet 的源码分析其 Hash 存储机制
通过 HashMap.HashSet 的源码分析其 Hash 存储机制 集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并非真正的把 Java 对象放入数组中.仅仅是把对象的 ...
随机推荐
- 集合、增强for、泛型
Collection集合:Collection是层次结构中的根接口,存储的元素为对象,(也就是说只能存储引用数据类型,不能存储基础数据类型),具体可查询API.集合与数组的区别:1.集合只能存放引用数 ...
- # openVPN+LDAP AD认证,组权限管理
# openVPN+LDAP AD认证,组权限管理 原创内容http://www.cnblogs.com/elvi/p/7661178.html # openVPN+LDAP AD认证,组权限管理 # ...
- linux上安装php7 memcache扩展 和 安装服务端memcached
linux上安装memcached不算太困难.唯一让本人感到困难的是 php7的memcache扩展安装.真的蛋疼! 先说安装服务端 memcached 1. 首先安装Libevent事件触发管理器. ...
- Java分布式锁实现详解
在进行大型网站技术架构设计以及业务实现的过程中,多少都会遇到需要使用分布式锁的情况.那么问题也就接踵而至,哪种分布式锁更适合我们的项目? 下面就这个问题,我做了一些分析: 分布式锁现状: 目前几乎很多 ...
- python3 三级菜单-基础版
# -*- coding:utf-8 -*- data = { "北京":{ "东城区":{ "安定门":["国子监", ...
- TCP程序中发送和接收数据
这里我们来探讨一下在网络编程过程中,有关read/write 或者send/recv的使用细节.这里有关常用的阻塞/非阻塞的解释在网上有很多很好的例子,这里就不说了,还有errno ==EAGAIN ...
- 运行期以索引获取tuple元素-C++14
在编译期很容易根据索引来获取对应位置的元素,因为 tuple 的帮助函数 std::get<N>(tp) 就能获取 tuple 中第 N 个元素.然而我们却不能直接在运行期通过变量来获取 ...
- 用KMP算法实现strStr()
strStr()函数的用途是在一个字符串S中寻找某个字串P第一次出现的位置.并返回其下标,找不到时返回-1.最简单的办法就是找出S全部的子串和P进行比較,然而这种方法比較低效.假设我们从S的下标0和P ...
- 近期对FTP及Excel数据处理的一些摸索
一个多月没写随笔了,主要是发的东西,自己感觉也很垃圾,说又说回来,谁不是从垃圾变强的,所以不比比,还得努力.come on!! Python学习也有段时间了,近期为了解决同事的一个难题,所以我们决定联 ...
- Python笔记·第二章—— Python的编码问题(一)
一.什么是编码 可以说,计算机是一个即聪明又笨蛋的家伙.说它聪明,是因为他可以做很多事情,它的强大无需多说,大家应该都有所了解以及感受.但是为什么说它又是个笨蛋呢,因为我们在电脑上写出的每一个字,保存 ...