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

JavaScript中Map與Object應(yīng)用場景

 更新時(shí)間:2022年07月07日 14:06:07   作者:夏目里繪  
這篇文章主要為大家介紹了JavaScript中Map與Object應(yīng)用場景的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

在 JavaScript 中選擇 Object 和 Map 的缺失指南

在 JavaScript 中,對象很方便。它們使我們能夠輕松地將多條數(shù)據(jù)組合在一起。在 ES6 之后,我們?yōu)樵撜Z言添加了一個(gè)新功能 - Map. 在很多方面,它似乎比Object更強(qiáng)大,但界面有點(diǎn)笨拙。然而,大多數(shù)人在需要哈希映射時(shí)仍然會(huì)使用對象,并且只有在他們意識(shí)到鍵不能只是他們用例的字符串時(shí)才切換到使用。因此,在當(dāng)今的 JavaScript 社區(qū)中Map仍未得到充分利用。

在這篇文章中,我將分解您應(yīng)該考慮使用Mapmore 的所有原因及其性能特征與基準(zhǔn)測試。

在 JavaScript 中,Object 是一個(gè)相當(dāng)寬泛的術(shù)語。幾乎所有東西都可以是一個(gè)對象,除了兩種底部類型 -nullundefined. 在這篇博文中,Object 僅指普通的舊對象,由左大括號(hào){和右大括號(hào)分隔}

博士

  • 用于Object在作者時(shí)已知的屬性/字段數(shù)量固定且有限的記錄,例如配置對象。以及一般一次性使用的任何東西。
  • 用于Map字典或哈希映射,其中條目數(shù)量可變,更新頻繁,其鍵在作者時(shí)可能未知,例如事件發(fā)射器。
  • 根據(jù)我的基準(zhǔn)測試,除非鍵是小整數(shù)字符串,否則在插入、刪除和迭代速度Map方面確實(shí)比Object高效,并且它消耗的內(nèi)存Object相同大小的要少。

為什么 Object 缺少哈希映射

將對象用于哈希映射最明顯的缺點(diǎn)可能是對象只允許字符串和符號(hào)作為鍵。任何其他類型都將通過toString將這些方法隱式轉(zhuǎn)換為字符串。

const foo = []
const bar = {}
const obj = {[foo]: 'foo', [bar]: 'bar'}
console.log(obj) // {"": 'foo', [object Object]: 'bar'}

更重要的是,將對象用于哈希映射可能會(huì)導(dǎo)致混淆和安全隱患。

不需要的繼承

在 ES6 之前,獲取哈希映射的唯一方法是創(chuàng)建一個(gè)空對象。

const hashMap = {}

但是,在創(chuàng)建后,此對象不再為空。雖然hashMap是用一個(gè)空的對象字面量制作的,但它會(huì)自動(dòng)繼承自Object.prototype. 這就是為什么我們可以調(diào)用像hasOwnPropertytoStringconstructoron這樣的方法,hashMap即使我們從未在對象上明確定義這些方法。

由于原型繼承,我們現(xiàn)在混合了兩種類型的屬性:存在于對象本身中的屬性,即它自己的屬性,以及存在于原型鏈中的屬性,即繼承的屬性。因此,我們需要額外的檢查(例如hasOwnProperty)來確保給定的屬性確實(shí)是用戶提供的,而不是從原型繼承的。

最重要的是,由于屬性解析機(jī)制在 JavaScript 中的工作方式 Object.prototype,運(yùn)行時(shí)的任何更改都會(huì)在所有對象中產(chǎn)生連鎖反應(yīng)。 這為原型污染攻擊打開了大門,這對于大型 JavaScript 應(yīng)用程序來說可能是一個(gè)嚴(yán)重的安全問題。

幸運(yùn)的是,我們可以通過使用來解決這個(gè)問題Object.create(null),這會(huì)生成一個(gè)不繼承任何內(nèi)容的對象Object.prototype。

名稱沖突

當(dāng)一個(gè)對象自己的屬性與其原型上的屬性發(fā)生名稱沖突時(shí),它會(huì)破壞預(yù)期并因此使您的程序崩潰。

例如,我們有一個(gè)foo接受對象的函數(shù):

function foo(obj) {
	//...
	for(const key in obj) {
		if(obj.hasOwnProperty(key)) {
		}
	}
}

存在可靠性風(fēng)險(xiǎn)obj.hasOwnProperty(key):考慮到屬性解析機(jī)制在 JavaScript 中的工作方式,如果obj包含用戶提供的同名屬性,則會(huì)hasOwnProperty隱藏Object.prototype.hasOwnProperty. 結(jié)果,我們不知道在運(yùn)行時(shí)會(huì)準(zhǔn)確調(diào)用哪個(gè)方法。

可以進(jìn)行一些防御性編程來防止這種情況。例如,我們可以借用hasOwnProperty來代替: Object.prototype

function foo(obj) {
	//...
	for(const key in obj) {
		if(Object.prototype.hasOwnProperty.call(obj, key)) {
			// ...
		}
	}
}

一種更短的方法可能是在對象文字上調(diào)用該方法,{}.hasOwnProperty.call(key)但它仍然很麻煩。這就是為什么有一個(gè)新添加的靜態(tài)方法Object.hasOwn。

尺寸

Object沒有提供方便的 API 來獲取大小,即屬性的數(shù)量。構(gòu)成對象大小的因素也有細(xì)微差別:

  • 如果您只關(guān)心字符串、可枚舉鍵,那么您可以將鍵轉(zhuǎn)換為數(shù)組Object.keys()并獲取其length.
  • 如果您想考慮不可枚舉的字符串鍵,那么您必須使用Object.getOwnPropertyNames來獲取鍵列表并獲取其長度。
  • 如果您對符號(hào)鍵感興趣,可以使用getOwnPropertySymbols來顯示符號(hào)鍵?;蛘?,您可以使用Reflect.ownKeys 同時(shí)獲取字符串鍵和符號(hào)鍵,無論它是否可枚舉。

迭代

遍歷對象也有類似的復(fù)雜性。

我們可以使用良好的舊for...in循環(huán)。但它揭示了繼承的可枚舉屬性:

Object.prototype.foo = 'bar'
const obj = {id: 1} 
for(const key in obj) {
	console.log(key) // 'id', 'foo'
}

我們不能用for...of與對象一起使用,因?yàn)槟J(rèn)情況下它不是可迭代的,除非我們用Symbol.iterator在其上顯式定義方法。

我們可以使用Object.keys,Object.valuesObject.entries來獲取可枚舉的字符串鍵(或/和值)列表,然后對其進(jìn)行迭代,這會(huì)引入額外的開銷步驟。

最后,臭名昭著的插入順序沒有得到充分尊重。在大多數(shù)瀏覽器中,整數(shù)鍵按升序排序并且優(yōu)先于字符串鍵,即使字符串鍵插入到整數(shù)鍵之前也是如此。

const obj = {}
obj.foo = 'first'
obj[2] = 'second'
obj[1] = 'last'
console.log(obj) // {1: 'last', 2: 'second', foo: 'first'}

清除

沒有簡單的方法可以從對象中刪除所有屬性,您必須使用delete操作符一個(gè)一個(gè)地刪除每個(gè)屬性,這在歷史上被認(rèn)為是緩慢的。但是我的基準(zhǔn)測試表明,它的性能實(shí)際上比不上Map.prototype.delete

檢查屬性是否存在

最后,我們不能依賴點(diǎn)/括號(hào)符號(hào)來檢查屬性是否存在,因?yàn)橹当旧砜梢栽O(shè)置為undefined. 相反,我們必須使用Object.prototype.hasOwnProperty和 Object.hasOwn

const obj = {a: undefined}
Object.hasOwn(obj, 'a') // true

哈希映射的映射

ES6 帶來了 Map。它更適合哈希映射。

首先,與Object只允許字符串和符號(hào)作為鍵不同,它Map支持任何數(shù)據(jù)類型的鍵。

但是,如果您Map用于存儲(chǔ)對象的元數(shù)據(jù),那么您應(yīng)該使用它WeakMap來避免內(nèi)存泄漏。

但更重要的是,Map它提供了用戶定義和內(nèi)置程序數(shù)據(jù)之間的清晰分離,但代價(jià)是額外的檢索條目。

Map還提供了更好的人體工程學(xué):Map默認(rèn)情況下,A 是可迭代的。這意味著您可以使用for...of輕松迭代地圖,并執(zhí)行諸如使用嵌套解構(gòu)從地圖中提取第一個(gè)條目之類的操作。

const [[firstKey, firstValue]] = map

Object相比Map為各種常見任務(wù)提供專用 API:

Map.prototype.has檢查給定條目的存在,Object.prototype.hasOwnProperty/Object.hasOwn在對象上相比不那么尷尬

Map.prototype.get返回與提供的鍵關(guān)聯(lián)的值。人們可能會(huì)覺得這比對象上的點(diǎn)表示法或括號(hào)表示法更笨拙。然而,它在用戶數(shù)據(jù)和內(nèi)置方法之間提供了清晰的分離。

Map.prototype.size返回 a 中的條目數(shù),Map它顯然是獲得對象大小所必須執(zhí)行的操作的贏家。此外,它要快得多。

Map.prototype.clear刪除 a 中的所有條目,Map它比運(yùn)算符delete快得多。

性能

在大多數(shù)情況下,JavaScript 社區(qū)似乎普遍認(rèn)為MapObject好. 有些人聲稱要通過Object切換到Map.

我磨練 Leetcode 的經(jīng)驗(yàn)似乎證實(shí)了這個(gè)信念:Leetcode 將大量數(shù)據(jù)作為測試用例提供給您的解決方案,如果您的解決方案耗時(shí)過長,它就會(huì)超時(shí)。像這樣的問題只有在你使用Object時(shí)才會(huì)超時(shí),而不是在Map.

但是,我相信只是說“Map比對象更快”是簡化的。一定有一些細(xì)微差別是我想自己找出來的。所以。我構(gòu)建了一個(gè)小應(yīng)用程序來運(yùn)行一些基準(zhǔn)測試。

基準(zhǔn)測試實(shí)施細(xì)節(jié)

該應(yīng)用程序有一個(gè)表格,顯示在ObjectMap 上測量的插入、迭代和刪除速度。

插入和迭代的性能以每秒操作數(shù)來衡量。我編寫了一個(gè) util 函數(shù)measureFor,它重復(fù)運(yùn)行目標(biāo)函數(shù),直到達(dá)到指定的最小時(shí)間閾值(即durationUI 上的輸入字段)。它返回每秒執(zhí)行此類函數(shù)的平均次數(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);
}

至于刪除,我只是要測量使用delete運(yùn)算符從對象中刪除所有屬性所需的時(shí)間,并將其與 Map.prototype.delete相同大小的 Map 的時(shí)間進(jìn)行比較。我可以使用Map.prototype.clear,但它違背了基準(zhǔn)測試的目的,因?yàn)槲掖_信它會(huì)更快。

在這三個(gè)操作中,我更加關(guān)注插入,因?yàn)樗俏以谌粘9ぷ髦袌?zhí)行的最常見的操作。對于迭代性能,很難提出一個(gè)包羅萬象的基準(zhǔn),因?yàn)槲覀兛梢栽诮o定對象上執(zhí)行許多不同的迭代變體。這里我只測量for ... in循環(huán)。

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

  • 字符串,例如yekwl7caqejth7aawelo4.
  • 整數(shù)字符串,例如123.
  • 由 生成的數(shù)字字符串Math.random().toString(),例如0.4024025689756525

所有的鍵都是隨機(jī)生成的,所以我們不會(huì)碰到 V8 實(shí)現(xiàn)的內(nèi)聯(lián)緩存。我還顯式地將整數(shù)和數(shù)字鍵轉(zhuǎn)換為字符串,然后再將它們添加到對象以避免隱式轉(zhuǎn)換的開銷。

最后,在基準(zhǔn)測試開始之前,還有一個(gè)至少 100 毫秒的預(yù)熱階段,我們反復(fù)創(chuàng)建新的對象和地圖,這些新對象和地圖會(huì)立即被丟棄。

如果你想玩,我把代碼放在Codesandbox上。

我從100個(gè)屬性/條目的Object和Map開始,一直到5000000,讓每種類型的操作持續(xù)運(yùn)行10000ms,看看它們彼此之間的表現(xiàn)如何。以下是我的發(fā)現(xiàn)……

為什么我們在條目數(shù)達(dá)到 5000000 時(shí)停止?

字符串鍵

一般來說,當(dāng)鍵是(非數(shù)字)字符串時(shí),在所有操作上都Map優(yōu)于Object

但細(xì)微差別在于,當(dāng)條目數(shù)量不是很大(低于 100000)時(shí),Map插入速度是Object插入速度的兩倍,但隨著大小增長超過 100000,性能差距開始縮小。

我制作了一些圖表來更好地說明我的發(fā)現(xiàn)。

上圖顯示了隨著條目數(shù)量的增加(x 軸),插入率如何下降(y 軸)。但是因?yàn)?X 軸擴(kuò)展得太寬(從 100 到 1000000),所以很難分辨這兩條線之間的差距。

然后我使用對數(shù)刻度來處理數(shù)據(jù)并制作下面的圖表。

您可以清楚地看出兩條線正在匯合。

我制作了另一個(gè)圖表,繪制了MapObject插入速度相關(guān)的速度。您可以看到Map開始時(shí)比Object快. 然后隨著時(shí)間的推移,性能差距開始縮小。Map隨著規(guī)模增長到 5000000,最終速度僅快 30%。

但是,我們大多數(shù)人在一個(gè)對象或映射中永遠(yuǎn)不會(huì)有超過 100 萬個(gè)條目。具有數(shù)百或數(shù)千個(gè)條目的大小.  因此,我們是否應(yīng)該把它留在那兒,然后全力以赴開始重構(gòu)我們的代碼庫Map?

絕對不會(huì)……或者至少?zèng)]有期望我們的應(yīng)用程序會(huì)快 2 倍。請記住,我們還沒有探索過其他類型的鍵。讓我們看一下整數(shù)鍵。

整數(shù)鍵

我特別想對具有整數(shù)鍵的對象運(yùn)行基準(zhǔn)測試的原因是 V8 在內(nèi)部優(yōu)化了整數(shù)索引屬性并將它們存儲(chǔ)在可以線性和連續(xù)訪問的單獨(dú)數(shù)組中。我找不到任何資源來確認(rèn)它對Map 采用了相同類型的優(yōu)化。

讓我們首先嘗試 [0, 1000] 范圍內(nèi)的整數(shù)鍵。

正如我所料,這次Object 跑贏大盤。  Object插入速度比Map快 65%,迭代速度快 16%。

讓我們擴(kuò)大范圍,使鍵中的最大整數(shù)為 1200。

現(xiàn)在似乎Map開始比Object快一點(diǎn)

現(xiàn)在我們只增加了整數(shù)鍵的范圍,而不是和的實(shí)際Object大小Map。讓我們增大尺寸,看看它如何影響性能。

當(dāng)屬性大小為1000時(shí),Object的插入速度比Object快70%,迭代速度比Map慢2倍。

我嘗試了許多不同的Object/Object大小和整數(shù)鍵范圍的組合,但未能想出一個(gè)明確的模式。但我看到的總體趨勢是,隨著大小的增長,使用一些相對較小的整數(shù)作為鍵,Object在插入方面的性能可以比map更好,總是與刪除大致相同,迭代速度是map的4到5倍。最大整數(shù)鍵的閾值,即Object在插入時(shí)開始變慢的閾值,將隨著Object的大小而增長。例如,當(dāng)該Object只有100個(gè)表項(xiàng)時(shí),閾值為1200;當(dāng)它有10000個(gè)條目時(shí),閾值似乎在24000左右。

數(shù)字鍵

最后,我們來看看最后一種鍵——數(shù)字鍵。

從技術(shù)上講,以前的整數(shù)鍵也是數(shù)字的。這里的數(shù)字鍵特指生成的數(shù)字字符串Math.random().toString()。

結(jié)果與字符串-鍵的情況類似:map開始時(shí)比Object快得多(插入和刪除快2倍,迭代快4-5倍),但隨著大小的增加,增量越來越小。

嵌套對象/地圖呢?

內(nèi)存使用情況

基準(zhǔn)測試的另一個(gè)重要方面是內(nèi)存利用率。

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

我創(chuàng)建了一個(gè)小腳本來測量它們各自的內(nèi)存使用情況,并在每次測量中手動(dòng)觸發(fā)完全垃圾收集。運(yùn)行它,node --expose-gc我得到以下結(jié)果:

{
  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消耗的內(nèi)存比Object少20%到50%,這并不奇怪,因?yàn)?code>Map它不存儲(chǔ)屬性描述符,例如writable// like 。enumerable``configurable``Object

結(jié)論

那么我們能從這一切中得到什么?

  •  MapObject更快,除非您有小的整數(shù)和數(shù)組索引鍵,而且它的內(nèi)存效率更高。
  • 如果需要經(jīng)常更新的哈希映射,可以使用Map; 如果你想要一個(gè)固定的鍵值集合(例如記錄),請使用Object,并注意原型繼承帶來的陷阱。

如果您確切了解 V8 如何優(yōu)化的細(xì)節(jié),Map或者只是想指出我的基準(zhǔn)測試中的缺陷,請聯(lián)系我。我很樂意根據(jù)您的信息更新這篇文章!

瀏覽器兼容性注意事項(xiàng)

Map是 ES6 的一個(gè)特性。到目前為止,我們大多數(shù)人都不應(yīng)該擔(dān)心它的兼容性,除非你的目標(biāo)用戶群是一些小眾的舊瀏覽器。“舊”是指比 IE 11 更早,因?yàn)榧词?IE 11 也支持Map而此時(shí) IE 11已死。我們不應(yīng)該在默認(rèn)情況下盲目地轉(zhuǎn)譯和添加 polyfill 到目標(biāo) ES5,因?yàn)樗粌H會(huì)膨脹你的包大小,而且與現(xiàn)代 JavaScript 相比運(yùn)行起來很慢。最重要的是,它會(huì)懲罰 99.999% 的使用現(xiàn)代瀏覽器的用戶。

另外,我們不必放棄對舊版瀏覽器的支持——nomodule通過提供后備包來提供舊版代碼,這樣我們就可以避免使用現(xiàn)代瀏覽器降低訪問者的體驗(yàn)。

JavaScript 語言在不斷發(fā)展,平臺(tái)在優(yōu)化現(xiàn)代 JavaScript 方面也越來越好。我們不應(yīng)該以瀏覽器兼容性為借口忽略所有已做出的改進(jìn)。

原文翻譯于https://www.zhenghao.io/posts/object-vs-map

以上就是JavaScript中Map與Object應(yīng)用場景的詳細(xì)內(nèi)容,更多關(guān)于JavaScript中Map Object的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 在博客園博文中添加自定義右鍵菜單的方法詳解

    在博客園博文中添加自定義右鍵菜單的方法詳解

    本文是DOM鼠標(biāo)事件的一個(gè)實(shí)際應(yīng)用。查看博客園的博客文章時(shí),有的文章非常長,卻沒有回到頂部按鈕;而且文章的點(diǎn)贊和評論都在文章最底部,使用時(shí)并不方便。所以使用自定義右鍵菜單來實(shí)現(xiàn)回到頂部、點(diǎn)贊、評論這三個(gè)主要功能,需要的朋友可以參考下
    2020-02-02
  • 微信小程序 rpx 尺寸單位詳細(xì)介紹

    微信小程序 rpx 尺寸單位詳細(xì)介紹

    這篇文章主要介紹了微信小程序 rpx尺寸單位以及樣式詳細(xì)介紹的相關(guān)資料,有效的幫助大家開發(fā)微信小程序,避免手機(jī)尺寸問題,需要的朋友可以參考下
    2016-10-10
  • Umi4集成阿里低代碼框架lowcode-engine實(shí)現(xiàn)

    Umi4集成阿里低代碼框架lowcode-engine實(shí)現(xiàn)

    這篇文章主要為大家介紹了Umi4集成阿里低代碼框架lowcode-engine實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • 微信小程序(十六)form組件詳細(xì)介紹

    微信小程序(十六)form組件詳細(xì)介紹

    這篇文章主要介紹了微信小程序form組件詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下
    2016-09-09
  • 4個(gè)頂級開源JavaScript圖表庫

    4個(gè)頂級開源JavaScript圖表庫

    今天小編就為大家分享一篇關(guān)于4個(gè)頂級開源JavaScript圖表庫,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-09-09
  • 前端通過JavaScript創(chuàng)建修改CAD圖形詳情

    前端通過JavaScript創(chuàng)建修改CAD圖形詳情

    這篇文章介紹JavaScript創(chuàng)建修改CAD圖形,創(chuàng)建修改CAD圖形,一般是基于AutoCAD進(jìn)行二次開發(fā),ObjectARX是AutoDesk公司針對AutoCAD平臺(tái)上的二次開發(fā)而推出的一個(gè)開發(fā)軟件包,它提供了以C++為基礎(chǔ)的面向?qū)ο蟮拈_發(fā)環(huán)境及應(yīng)用程序接口,能真正快速的訪問AutoCAD圖形數(shù)據(jù)庫
    2021-10-10
  • javascript的setTimeout()使用方法總結(jié)

    javascript的setTimeout()使用方法總結(jié)

    這篇文章主要給大家分享javascript的setTimeout()使用方法總結(jié),js的setTimeout方法用處比較多,通常用在頁面刷新了、延遲執(zhí)行了等等,下面我們一起來看看文章對該內(nèi)容的具體總結(jié)吧,需要的朋友可以參考一下
    2021-11-11
  • 微信小程序 教程之模塊化

    微信小程序 教程之模塊化

    這篇文章主要介紹了微信小程序 模塊化的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • 微信小程序 兩種為對象屬性賦值的方式詳解

    微信小程序 兩種為對象屬性賦值的方式詳解

    這篇文章主要介紹了微信小程序 兩種為對象屬性賦值的方式詳解的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • 13個(gè)JavaScript 一行程序,讓你看起來就是個(gè)專家

    13個(gè)JavaScript 一行程序,讓你看起來就是個(gè)專家

    JavaScript 可以做很多好玩的事, 從復(fù)雜的框架到處理API,有太多的東西需要學(xué)習(xí)。但是,它也能讓我們只用一行就能做一些了不起的事情。今天的文章小編就為大家介紹13 個(gè)JavaScript 行程序,需要的朋友可以參考下
    2021-08-08

最新評論