lodash源码分析之List缓存
昨日我沿着河岸/漫步到/芦苇弯腰喝水的地方
顺便请烟囱/在天空为我写一封长长的信
潦是潦草了些/而我的心意/则明亮亦如你窗前的烛光/稍有暧昧之处/势所难免/因为风的缘故
——洛夫《因为风的缘故》
本文为读 lodash 源码的第七篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash
gitbook也会同步仓库的更新,gitbook地址:pocket-lodash
作用与用法
在之前的《lodash源码分析之Hash缓存》介绍过用 Hash 做缓存的情况,在这篇文章中介绍过,lodash 是想要实现和 Map 一样的接口。
Hash 其实是用对象来做缓存,但是对象有一个局限,它的 key 只能是字符串或者 Symbol 类型,但是 Map 是支持各种类型的值来作为 key,因此 Hash 缓存无法完全模拟 Map 的行为,当遇到 key 为数组、对象等类型时,Hash 就无能为力了。
因此,在不支持 Map 的环境下,lodash 实现了 ListCache 来模拟,ListCache 本质上是使用一个二维数组来储存数据。
ListCache 的调用方式和 Hash 一致:
new ListCache([
[{key: 'An Object Key'}, 1],
[['An Array Key'],2],
[function(){console.log('A Function Key')},3]
])
返回的结果如下:
{
size: 3,
__data__: [
[{key: 'An Object Key'}, 1],
[['An Array Key'],2],
[function(){console.log('A Function Key')},3]
]
}
结构和 Hash 类似,但是 __data__ 变成了数组。
接口设计
ListCache 的接口与 Hash 一样,同样实现了 Map 的数据管理接口。

依赖
import assocIndexOf from './assocIndexOf.js'
源码分析
class ListCache {
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__ = []
this.size = 0
}
delete(key) {
const data = this.__data__
const index = assocIndexOf(data, key)
if (index < 0) {
return false
}
const lastIndex = data.length - 1
if (index == lastIndex) {
data.pop()
} else {
data.splice(index, 1)
}
--this.size
return true
}
get(key) {
const data = this.__data__
const index = assocIndexOf(data, key)
return index < 0 ? undefined : data[index][1]
}
has(key) {
return assocIndexOf(this.__data__, key) > -1
}
set(key, value) {
const data = this.__data__
const index = assocIndexOf(data, key)
if (index < 0) {
++this.size
data.push([key, value])
} else {
data[index][1] = value
}
return this
}
}
constructor
构造器跟 Hash 一模一样,都是先调用 clear 方法,然后调用 set 方法,往缓存中加入初始数据。
这里调用 clear 方法并不是说为了清除数据,还没开始使用这个类,肯定是没有数据的,而是为了初始化 __data__ 和 size 这两个属性。
clear
clear() {
this.__data__ = []
this.size = 0
}
clear 是为了清空缓存。
其实就是将容器 __data__ 设置成空数组,在 Hash 中是设置为空对象,将缓存数量 size 设置为 0 。
has
has(key) {
return assocIndexOf(this.__data__, key) > -1
}
has 用来判断是否已经有缓存数据,如果缓存数据已经存在,则返回 true 。
在之前的文章中已经介绍过,assocIndexOf 检测的是对应 key 的 [key,value] 数组在二维数组中的索引,其行为跟 indexOf 一致,不存在于二维数组中时,返回 -1 ,否则返回索引值。因此可以用是否大于 -1 来判断指定 key 的数据是否已经被缓存。
set
set(key, value) {
const data = this.__data__
const index = assocIndexOf(data, key)
if (index < 0) {
++this.size
data.push([key, value])
} else {
data[index][1] = value
}
return this
}
set 用来增加或者更新需要缓存的值。set 的时候需要同时维护 size 和缓存的值。
跟 has 一样,调用 assocIndexOf 找到指定 key 的索引值,如果小于 0 ,则表明指定的 key 尚未缓存,需要将缓存数量 size 加 1 ,然后将缓存数据加入到 this.__data__ 的末尾。
否则更新 value 即可。
强迫症看到 has 用大于 -1 来判断,而这里用小于 0 来判断可能会相当难受。
get
get(key) {
const data = this.__data__
const index = assocIndexOf(data, key)
return index < 0 ? undefined : data[index][1]
}
get 方法是从缓存中取值。
如果缓存中存在值,则返回缓存中的值,否则返回 undefined 。
delete
delete(key) {
const data = this.__data__
const index = assocIndexOf(data, key)
if (index < 0) {
return false
}
const lastIndex = data.length - 1
if (index == lastIndex) {
data.pop()
} else {
data.splice(index, 1)
}
--this.size
return true
}
delete 方法用来删除指定 key 的缓存。成功删除返回 true, 否则返回 false。 删除操作同样需要维护 size 属性。
首先调用 assocIndexOf 来找到缓存的索引。
如果索引小于 0 ,表明没有缓存,删除不成功,直接返回 false 。
如果要删除的缓存是缓存中的最后一项,则直接调用 pop 方法,将缓存删除,否则将调用 splice 方法将对应位置的缓存删除。
为什么不直接都用 splice 来删除数据呢?因为 pop 的性能比 splice 好,我简单测了一下,大概快 17% 左右。
有兴趣的可以看下 pop 和 splice 的规范,splice 要比 pop 做的事情要多。
从这里又看出了 lodash 对性能的极致追求。
最后将缓存数量 size 减少 1 。
参考
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见: 
作者:对角另一面
lodash源码分析之List缓存的更多相关文章
- lodash源码分析之Hash缓存
在那小小的梦的暖阁,我为你收藏起整个季节的烟雨. --洛夫<灵河> 本文为读 lodash 源码的第四篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash gitbo ...
- lodash源码分析之缓存方式的选择
每个人心里都有一团火,路过的人只看到烟. --<至爱梵高·星空之谜> 本文为读 lodash 源码的第八篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash gitb ...
- lodash源码分析之缓存使用方式的进一步封装
在世界上所有的民族之中,支配着他们的喜怒选择的并不是天性,而是他们的观点. --卢梭<社会与契约论> 本文为读 lodash 源码的第九篇,后续文章会更新到这个仓库中,欢迎 star:po ...
- lodash源码分析之自减的两种形式
这个世界需要一个特定的恶人,可以供人们指名道姓,千夫所指:"全都怪你". --村上春树<当我谈跑步时我谈些什么> 本文为读 lodash 源码的第六篇,后续文章会更新到 ...
- lodash源码分析之数组的差集
外部世界那些破旧与贫困的样子,可以使我内心世界得到平衡. --卡尔维诺<烟云> 本文为读 lodash 源码的第十七篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodas ...
- Solr4.8.0源码分析(19)之缓存机制(二)
Solr4.8.0源码分析(19)之缓存机制(二) 前文<Solr4.8.0源码分析(18)之缓存机制(一)>介绍了Solr缓存的生命周期,重点介绍了Solr缓存的warn过程.本节将更深 ...
- Solr4.8.0源码分析(18)之缓存机制(一)
Solr4.8.0源码分析(18)之缓存机制(一) 前文在介绍commit的时候具体介绍了getSearcher()的实现,并提到了Solr的预热warn.那么本文开始将详细来学习下Solr的缓存机制 ...
- lodash源码分析之baseFindIndex中的运算符优先级
我悟出权力本来就是不讲理的--蟑螂就是海米:也悟出要造反,内心必须强大到足以承受任何后果才行. --北岛<城门开> 本文为读 lodash 源码的第十篇,后续文章会更新到这个仓库中,欢迎 ...
- jQuery 源码分析(十) 数据缓存模块 data详解
jQuery的数据缓存模块以一种安全的方式为DOM元素附加任意类型的数据,避免了在JavaScript对象和DOM元素之间出现循环引用,以及由此而导致的内存泄漏. 数据缓存模块为DOM元素和JavaS ...
随机推荐
- Shell编程中的变量作用域
有两个shell脚本文件: a.sh name=Tom echo $name ./b.sh b.sh echo "name in b.sh:"$name 运行./a.sh时输出结果 ...
- Asp.Net Web API(六)
Asp.Net Web API不可以需要IIS.可以自己在主机上承载一个Web API 创建WebAPI.Server项目 创建一个控制器项目的服务端 在Nuget中添加Microsoft.AspNe ...
- ligerUI---ligerForm中下拉框使用
写在前面: 最近项目的前框框架用的是ligerUI,一开始我是拒绝的,因为貌似ligerUI很少有人用,我真的很想问我们team的斌哥哥为什么要用ligerUI来做前端框架?????(啊哈哈哈,用什么 ...
- MySQL相关命令
我们现在初步学习MySQL,下面来简单介绍一下MySQL常用命令 版权归skylinelin所有,转载请注明出处. 首先登录MySQL数据库,"\h"一下看一下帮助信息 这些就是M ...
- ES6的数据类型
首先ES6包含了ES5里面的数据类型,有undefined,null,boolean,String,Number,Object,而ES6又新增了一种数据类型是Symbol,这种的Symbol数据类型是 ...
- jquery如此强大,为什么还要写原生呢?
这是一个伪标题,其实是一篇年终总结. 在这家公司一年多,蛮多收获的.大部分来自自己,小部分来自公司. 做前端开发到现在,我觉得可以分为两部分. 前半部分做项目用原生js,jquery以及各种基于jq的 ...
- iOS PickerView选择视图
原文demo: @interface ViewController ()<UIPickerViewDelegate,UIPickerViewDataSource> { UIPickerVi ...
- Slf4j与其他日志系统兼容的使用
java生产的各种框架(如spring等)里各个框架会使用不同的日志体系,多个不同日志在一个jvm里混搭会出现一定问题 ,这里梳理一下java体系里常见的日志框架,以SFL4j为中心介绍下跟各个日志框 ...
- 深谈auto变量
1.c++中有一个关键字auto,c语言也有这么一个关键字,但是两者的意义大不相同. 2.c++中用auto定义的变量自动匹配赋值号右边的值的类型,具有自动匹配类型的作用,而c语言中auto只是声明一 ...
- Git安装和使用(谨记)
刚开始用git的小白适用,,转自http://www.cnblogs.com/qijunjun/p/7137207.html 实际项目开发中,我们经常会用一些版本控制器来托管自己的代码,今天就来总结下 ...