lodash源碼分析之Hash緩存

在那小小的夢的暖閣,我爲你收藏起整個季節的煙雨。javascript

——洛夫《靈河》java

本文爲讀 lodash 源碼的第四篇,後續文章會更新到這個倉庫中,歡迎 star:pocket-lodashgit

gitbook也會同步倉庫的更新,gitbook地址:pocket-lodashes6

做用與用法

Hash 顧名思義,就是要有一個離散的序列,根據 key 來儲取數據。而在 javascript 中,最適合的無疑是對象了。github

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__ 的對象中。ui

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 保持不變,不然 size1

緩存值其實就是設置緩存對象 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

參考

  1. Set 和 Map 數據結構
  2. Object.create()

License

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

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

做者:對角另外一面

相關文章
相關標籤/搜索