昨日我沿着河岸/漫步到/蘆葦彎腰喝水的地方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)
最後,全部文章都會同步發送到微信公衆號上,歡迎關注,歡迎提意見:
做者:對角另外一面