lodash源碼分析之List緩存

昨日我沿着河岸/漫步到/蘆葦彎腰喝水的地方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'

lodash源碼分析之自減的兩種形式

源碼分析

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 還沒有緩存,須要將緩存數量 size1 ,而後將緩存數據加入到 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% 左右。

有興趣的能夠看下 popsplice 的規範,splice 要比 pop 作的事情要多。

從這裏又看出了 lodash 對性能的極致追求。

最後將緩存數量 size 減小 1

參考

  1. Set 和 Map 數據結構
  2. MDN: 使用對象
  3. ECMAScript5.1中文版 + ECMAScript3 + ECMAScript(合集)

License

署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0)

最後,全部文章都會同步發送到微信公衆號上,歡迎關注,歡迎提意見:

做者:對角另外一面

相關文章
相關標籤/搜索