欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript?中什么時候使用?Map?更好

 更新時間:2022年08月24日 10:01:54   作者:前端黑板報???????  
這篇文章主要介紹了JavaScript中什么時候使用Map更好,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

Map 用作 Hash Map

ES6 給我們帶來了 Map,它更適合當做 hash map 的用例。

首先,它并不像 Object 那樣只允許 key 為 string 和 symobol,Map 的 key 支持任何數(shù)據(jù)類型。

可是如果你使用 Map 為對象存儲元數(shù)據(jù),應該使用 WeakMap 取而代之以此避免內(nèi)存泄漏。

更重要的是,Map 為用戶自定義和內(nèi)置對象屬性提供了清晰的界限,使用一個額外的方法 Map.prototype.get 獲取條目。

Map 同樣也提供了更人性化的方法,Map 默認就可迭代,意味著你可以輕松的與 for...of 一起使用,同樣使用嵌套的解構獲取第一個條目。

const [[firstKey, firstValue]] = map

與 Object 對比,Map 為各種常見任務提供了具體的方法:

  • Map.prototype.has 檢查一個給定條目的存在,與對象上的 Object.prototype.hasOwnProperty / Object.hasOwn 對比還是方便許多。
  • Map.prototype.get 返回與提供的 key 相關的值??赡軙腥擞X得比對象上的 . 和 [] 稍顯笨重。然后它為用戶自定義的數(shù)據(jù)和內(nèi)置的方法提供了清晰的界限。
  • Map.prototype.clear 可以清除 Map 上的所有條目且比 delete 操作更快。

性能

在 JavaScript 社區(qū)中似乎有一種共同的認知:Map 比 Object 快,在多數(shù)情況下,有些人在從 Object 切換到 Map 時看到了性能的提升。

從我在磨人的 LeetCode 中刷題的經(jīng)驗中似乎更加確認了這個觀點:LeetCode 使用了大量的數(shù)據(jù)來對你的解決方案做測試用例,若你的答案花費了太長時間則會超時。像這種問題一般在你使用 Object 時出現(xiàn)幾次,而 Map 則沒有。

可是,我相信只簡單的說 Map 比 Object 快很籠統(tǒng),肯定有一些細微的差別需要我去發(fā)現(xiàn)。因此,我創(chuàng)建了一個小應用來運行一些基準測試。

基準測試的實現(xiàn)細節(jié)

這個應用有一個表格用來展示分別對 Object 和 Map 作用插入、迭代和刪除的速度。

插入和迭代的性能是以每秒來測量的,我寫了一個工具方法 measureFor 用來重復執(zhí)行目標函數(shù),直到設定的最小閾值(就是傳入的 duration 值)。它返回的是每秒函數(shù)執(zhí)行的平均次數(shù)。

function measureFor(f, duration) {
  let iterations = 0;
  const now = performance.now();
  let elapsed = 0;
  while (elapsed < duration) {
    f();
    elapsed = performance.now() - now;
    iterations++;
  }

  return ((iterations / elapsed) * 1000).toFixed(4);
}

對于刪除,我打算簡單的對比一下從長度相同的 Object 、 Map 分別使用 delete 和 Map.protoype.delete 移除所有屬性所花費時間的對比。我知道有 Map.protype.clear 但據(jù)我所知它非常的快,這有悖于設置基礎測試的目的。

在這三種操作中,因為每天的工作中經(jīng)常使用到插入操作,所以我更關心它。對于迭代的性能,因為有很多方法用于對象的迭代,所以很難包含所有的基準測試。我在這里只測試了 for ... in 循環(huán)。

我這里使用了三種類型的 key:

  • 字符串,例如:'yekwl7caqejth7aawelo4'。
  • 整數(shù)字符串,例如:123。
  • 通過 Math.random().toString() 生產(chǎn)的數(shù)字字符串,例如:'0.6514674912156457'

所有的 key 都是隨機生成的,所以不會觸發(fā) V8 內(nèi)部實現(xiàn)的緩存機制。在把屬性添加到對象上之前,我還在顯性的把整數(shù)和數(shù)值類型的 key 通過 toString 轉換為字符串以此避免隱式開銷。

最后,在基準測試開始之前,還有一個 100ms 的熱身階段,就是重復創(chuàng)建新的 object 和 map 并立即丟棄掉所耗費的時間。

代碼放到了 CodeSandbox 如果你想試玩一下。

從 100 個屬性/條目大小的 Object 和 Map 開始,一直到5000000,每種類型的操作都執(zhí)行了 10000ms 來對比它們之間的表現(xiàn),

如下:

為何把條目數(shù)達到 5000000時才停止? 這是 JavaScript 中能得到的最大對象,根據(jù) StackOverflow 上一名活躍的 V8 工程師 @jmrk 所說:"如果 key 為字符串,當一個普通的對象元素達到 8.3M 時會各種對它的操作會變得非常慢(這是有技術原因的:某個位域有23位寬,當超過時采取非常緩慢的回退路徑。)"

字符串類型的鍵

通常來說,當 key 為字符串(非數(shù)值型),在各種操作中 Map 的表現(xiàn)勝過 Object

但是當條目數(shù)并沒有特別大的時候(100000 以下的時候),在插入操作的速度上 Map 基本是 Object 的兩倍,但當大小超過 100000 時,性能差距會開始縮小。

我制作了一個圖表來說明我的發(fā)現(xiàn):

上面的圖表演示了插入速率隨著條目數(shù)(x-axis)的增加是如何下降的(y-axis)??墒牵驗?x-axis 擴展的越來越大(從 100 到 1000000),很難區(qū)分出兩條線之間的間隔差距。

然后我用對數(shù)比例來處理數(shù)據(jù),做出了下面的圖表:

你會清楚的分不出兩條線漸漸地重疊。

我又用另一個圖表來展示當插入操縱時 Map 比 Object 快多少。你可以看到期初 Map 比 Object 快 2倍,隨著時間的推移性能差距開始縮小。最終,當大小達到 5000000 時,Map 只快了 30%。

我們的 object 和 map 的條目永遠不可能多余 1百萬。成百上千的條目,Map 至少比 Object 快 2 倍。因此,我們是否應該開始使用 Map 來重構我們的代碼?

當然不是,至少不能期望我們的程序變得比之前快 2倍。記住我們還沒有探索其它類型的 key,接下來讓我們一起看看整數(shù)類型的 key。

整數(shù)類型的鍵

我特別想運行對象上鍵為整數(shù)類型的原因是 V8 內(nèi)部為整數(shù)索引的屬性做了優(yōu)化以及把它們存儲在一個分開的數(shù)組中,然后可以線性和連續(xù)的獲取??墒俏覜]有找到任何資料來證明 V8 對 Map 做了同樣的優(yōu)化。

我們先在 [0,1000] 之間嘗試整數(shù)類型的 key。

就像我預期的一樣,這次 Object 比 Map 做得好,在插入方面快 65%以及循環(huán)方面快 16%。

讓我們把范圍調(diào)整到最大 1200。

現(xiàn)在看起來 Map 在插入方面快一些以及循環(huán)方便快 5 倍。

現(xiàn)在我們僅僅增加了鍵的范圍,而不是 Object 和 Map 的實際大小。讓我們來增加它們的大小看看如何影響性能。

當大小為 1000 時,插入方面 Object 比 Map 快 70% 以及循環(huán)方面快 2 倍。

我嘗試了很多種 Object / Map 大小與整數(shù)鍵范圍的組合但沒有得到一個清晰的模式。但我看到的一般趨勢是,隨著大小的增長,以一些相對較小的整數(shù)為鍵,對象在插入方面的性能比 Map 更強,在刪除方面總是大致相同,迭代速度要慢 4 或 5 倍。對象在插入時開始變慢的最大整數(shù)鍵的閾值會隨著對象的大小而增長。例如,當對象只有 100 個條目時,閾值是 1200 ;當它有 10000 個條目時,閾值似乎是 24000 左右。

數(shù)值類型的鍵

最后,我們一起看一看最后一種類型的鍵--數(shù)值型。

從技術上來說,前面的整數(shù)類型的鍵也是數(shù)值型,不過這里的數(shù)值型特指通過 Math.random().toString() 生成的數(shù)值字符串。

結果有點類似字符串類型的鍵:Map 開始時比 Object 快得多(插入和刪除快2倍,迭代快4-5倍),但隨著我們規(guī)模的增加,這個差距也越來越小。

嵌套的 Object / Map 呢? 你可能已經(jīng)發(fā)現(xiàn)了我只講了深度只有一層的 Object 和 Map。我確實增加了一些嵌套深度但是我發(fā)現(xiàn)只要總條目數(shù)相同性能特性大體一致,不管嵌套了多少層。

例如,我們有一個寬度為 100 和深度為 3 總數(shù)為 100 萬的條目,結果與寬度為 1000000 深度為 1 的性能幾乎相同。

內(nèi)存使用

基準測試的另一個重要因素為內(nèi)存利用。

由于我無法控制瀏覽器環(huán)境中的垃圾收集器,我決定在Node中運行基準測試。

我創(chuàng)建了一個小腳本來測量在每個測試中通過手動觸發(fā)完全垃圾回收時各自的內(nèi)存使用情況。

與 node --expose-gc 一起運行將會得到如下結果:

{
  object: {
    'string-key': {
      '10000': 3.390625,
      '50000': 19.765625,
      '100000': 16.265625,
      '500000': 71.265625,
      '1000000': 142.015625
    },
    'numeric-key': {
      '10000': 1.65625,
      '50000': 8.265625,
      '100000': 16.765625,
      '500000': 72.265625,
      '1000000': 143.515625
    },
    'integer-key': {
      '10000': 0.25,
      '50000': 2.828125,
      '100000': 4.90625,
      '500000': 25.734375,
      '1000000': 59.203125
    }
  },
  map: {
    'string-key': {
      '10000': 1.703125,
      '50000': 6.765625,
      '100000': 14.015625,
      '500000': 61.765625,
      '1000000': 122.015625
    },
    'numeric-key': {
      '10000': 0.703125,
      '50000': 3.765625,
      '100000': 7.265625,
      '500000': 33.265625,
      '1000000': 67.015625
    },
    'integer-key': {
      '10000': 0.484375,
      '50000': 1.890625,
      '100000': 3.765625,
      '500000': 22.515625,
      '1000000': 43.515625
    }
  }
}

很明顯,Map 比 Object 消耗的內(nèi)存少20%到50%不等,結果并不意外因為 Map 不存儲屬性的描述類似 Object 上的 writable 、enumerable 、configurable

總結

那么,我們從其中能得到什么?

  • Map 比 Object 更快,除非你有小的整數(shù)、數(shù)組索引為鍵,而且它更節(jié)省內(nèi)存。
  • 若 Hash Map 需要經(jīng)常更新你應該使用 Map;若你的集合為固定的鍵值(例如:記錄)則使用 Object,但是請注意原型繼承帶來的陷阱。

如果你知道 V8 優(yōu)化 Map 的細節(jié),或者只是想指出我的基準測試中的缺陷,請與我聯(lián)系。我很樂意根據(jù)你的信息來更新這個帖子。

瀏覽器兼容性筆記

Map 是 ES6 引入的特性,目前為止我們不需要擔心其兼容性除非你的客戶使用的舊瀏覽器。舊指比 IE11 版本低,即使 IE11 支持 Map 但它已經(jīng)  了。默認情況下,我們不需要費盡心思的添加轉換器和墊片來支持 ES5 ,因為那樣會增加你的打包體積,同樣與現(xiàn)代瀏覽器比更慢。最重要的是,那樣會對 99.999% 使用現(xiàn)代瀏覽器的用戶帶來負擔。

另外,我們不必放棄對傳統(tǒng)瀏覽器的支持--通過 nomodule 提供回退包來服務傳統(tǒng)代碼,這樣我們就可以避免降低使用現(xiàn)代瀏覽器的訪問者的體驗。如果你需要更多的說服力,請參考《過渡到現(xiàn)代JavaScript》。

JavaScript語言在不斷發(fā)展,平臺在優(yōu)化現(xiàn)代JavaScript方面也不斷完善。我們不應該以瀏覽器兼容性為借口,忽視所有已經(jīng)取得的改進。

到此這篇關于JavaScript 中什么時候使用 Map 更好的文章就介紹到這了,更多相關JavaScript 使用 Map 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論