昨日我沿着河岸/漫步到/芦苇弯腰喝水的地方javascript
顺便请烟囱/在天空为我写一封长长的信java
潦是潦草了些/而个人心意/则明亮亦如你窗前的烛光/稍有暧昧之处/势所不免/由于风的缘故git
——洛夫《由于风的缘故》es6
本文为读 lodash 源码的第七篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodashgithub
gitbook也会同步仓库的更新,gitbook地址:pocket-lodash数组
在以前的《lodash源码分析之Hash缓存》介绍过用 Hash
作缓存的状况,在这篇文章中介绍过,lodash 是想要实现和 Map
同样的接口。缓存
Hash
实际上是用对象来作缓存,可是对象有一个局限,它的 key
只能是字符串或者 Symbol
类型,可是 Map
是支持各类类型的值来做为 key
,所以 Hash
缓存没法彻底模拟 Map
的行为,当遇到 key
为数组、对象等类型时,Hash
就无能为力了。微信
所以,在不支持 Map
的环境下,lodash
实现了 ListCache
来模拟,ListCache
本质上是使用一个二维数组来储存数据。数据结构
ListCache
的调用方式和 Hash
一致:ide
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 } }
构造器跟 Hash
如出一辙,都是先调用 clear
方法,而后调用 set
方法,往缓存中加入初始数据。
这里调用 clear
方法并非说为了清除数据,还没开始使用这个类,确定是没有数据的,而是为了初始化 __data__
和 size
这两个属性。
clear() { this.__data__ = [] this.size = 0 }
clear
是为了清空缓存。
其实就是将容器 __data__
设置成空数组,在 Hash
中是设置为空对象,将缓存数量 size
设置为 0
。
has(key) { return assocIndexOf(this.__data__, key) > -1 }
has
用来判断是否已经有缓存数据,若是缓存数据已经存在,则返回 true
。
在以前的文章中已经介绍过,assocIndexOf
检测的是对应 key
的 [key,value]
数组在二维数组中的索引,其行为跟 indexOf
一致,不存在于二维数组中时,返回 -1
,不然返回索引值。所以能够用是否大于 -1
来判断指定 key
的数据是否已经被缓存。
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(key) { const data = this.__data__ const index = assocIndexOf(data, key) return index < 0 ? undefined : data[index][1] }
get
方法是从缓存中取值。
若是缓存中存在值,则返回缓存中的值,不然返回 undefined
。
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
。
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
最后,全部文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:
做者:对角另外一面