lodash源碼分析之Hash緩存

来源:https://www.cnblogs.com/hefty/archive/2018/01/02/8175620.html
-Advertisement-
Play Games

在那小小的夢的暖閣,我為你收藏起整個季節的煙雨。 ——洛夫《靈河》 本文為讀 lodash 源碼的第四篇,後續文章會更新到這個倉庫中,歡迎 star: "pocket lodash" gitbook也會同步倉庫的更新,gitbook地址: "pocket lodash" 作用與用法 顧名思義,就是要 ...


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

——洛夫《靈河》

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

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

作用與用法

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

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

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)

最後,所有文章都會同步發送到微信公眾號上,歡迎關註,歡迎提意見:

作者:對角另一面


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 以童話的方式深入淺出地講述了 Cortex M7 核心 MCU 的 ITCM 和 ICache 的原理、優勢和用法。 ...
  • Mysql中函數和存儲過程的區別 存儲過程: 1、 可以寫sql語句 2、 inout,out構造返回值 3、 調用:call:存儲過程名稱 4、 可以返回結果集 函數: 1、 不可以寫sql語句 2、 使用return 返回值 3、 調用時,使用函數名()即可 4、 不能獲取結果集 ...
  • 2.Orders訂單表 ...
  • ...
  • 資料庫設計範式是一個很重要的概念,但是這個重要程度只是適合於參考。使用資料庫設計範式,可以讓數據表更好的進行數據的保存,因為在合理的設計,如果數據量一大也肯定會存在性能上的問題,所以在開發中,唯一可以稱為設計的寶典——設計的時候儘量避免日後的程式出現多表關聯查詢。 第一範式 所謂的第一範式指的是數據... ...
  • 背景 App的開發一般都需要滿足Android和iOS兩個系統環境,也就意味著一個App需要定製兩套實現方案,造成開發成本和維護成本都很高。為瞭解決這個問題,最好的辦法就是實現一套代碼跨端運行,所以Hybrid App混合應用模式應運而生。在Hybrid App整個開發框架上,有各種各樣的框架,各種 ...
  • 如上圖,Runtime SDK是什麼東西?居然還有安卓、蘋果手機、Mac、QT的版本? 是不是意味著ArcGIS的編輯數據和空間分析可以通過編程的方法在每個平臺上滿地跑了? 答案是:是,也不是。 1. 與AO/AE的區別 AO是ArcGIS Desktop和ArcGIS Server的底層技術,有C ...
  • 前言:這一期的破解教程,有新的知識內容出現啦! 這一期破解的游戲是找不到之前的關鍵字,怎麼破解呢? 破解成功之後,添加一個Toast彈窗提示由XX破解,這操作該如何實現呢?請往下看~ 鏈接: https://pan.baidu.com/s/1dF8jKdF 密碼: 6666 破解步驟: 1.試玩,找 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...