JavaScript 中有了Object 為什么還需要 Map 呢
一、別把對象當 Map
1、可能通過原型鏈訪問到未定義的屬性
假設(shè)現(xiàn)有場景,開發(fā)一個網(wǎng)站,需要提供日語、漢語、韓語三種語言,我們可以定義一個字典去管理。
const dictionary = { 'ja': { 'Ninjas for hire': '忍者を雇う', }, 'zh': { 'Ninjas for hire': '忍者出租', }, 'ko': { 'Ninjas for hire': '고용 닌자', } } console.log(dictionary.ja['Ninjas for hire']) // 忍者を雇う console.log(dictionary.zh['Ninjas for hire']) // 忍者出租 console.log(dictionary.ko['Ninjas for hire']) // 고용 닌자
這樣我們就把不同語言的字典管理起來了。但是,當我們試圖訪問 constroctor
屬性,問題就出現(xiàn)了。
console.log(dictionary.ko['constructor']) // ƒ Object() { [native code] }
對于不存在的屬性,我們期望得到 undefined
,結(jié)果卻通過原型鏈訪問到了未定義的屬性,原型對象的 constructor
屬性,指向構(gòu)造函數(shù)。
此處有一個解決辦法是把原型設(shè)置為 null
Object.setPrototypeOf(dictionary.ko, null) console.log(dictionary.ko['constructor']) // undefined
2、對象的 Key 只能是字符串
假設(shè)需要將對象的 key
映射為 html
節(jié)點。我們寫如下代碼:
/* html部分 <div id="firstElement"></div> <div id="secondElement"></div> */ const firstElement = document.getElementById('firstElement') const secondElement = document.getElementById('secondElement') const map = {} map[firstElement] = { data: 'firstElement' } map[secondElement] = { data: 'secondElement' } console.log(map[firstElement].data) // secondElement console.log(map[secondElement].data) // secondElement
第一個元素的數(shù)據(jù)被覆蓋了,原因是對象中的 key 只能是字符串類型,當我們沒有使用字符串類型時,它會隱式調(diào)用 toString
() 函數(shù)進行轉(zhuǎn)換。于是兩個 html 元素都被轉(zhuǎn)為字符串 [object HTMLDivElement]
。
對象的鍵也可以為 Symbol
,不過在 for..in
遍歷和 Object.keys()
以及用 JSON.stringify()
進行序列化的時候,都會忽略為 Symbol
的鍵。
二、使用 Map
1、Map 常用操作
Map
可以使用任何 JavaScript
數(shù)據(jù)類型作為鍵
function People(name) { this.name = name } const zhangsan = new People('zhangsan') const xiaoming = new People('xiaoming') const lihua = new People('lihua') // 創(chuàng)建 Map const map = new Map() // 創(chuàng)建 Map 并進行初始化 將二維鍵值對數(shù)組轉(zhuǎn)換成一個Map對象 const map1 = new Map([ ['key1', 'val1'], ['key2', 'val2'], ]) // 將 Map 轉(zhuǎn)為二維數(shù)組 console.log(Array.from(map1)) // [ [ 'key1', 'val1' ], [ 'key2', 'val2' ] ] // 設(shè)置鍵值映射關(guān)系 map.set(zhangsan, { region: 'HB' }) map.set(xiaoming, { region: 'HN' }) // 根據(jù) key 獲取對應(yīng)值 console.log(map.get(zhangsan)) // { region: 'HB' } console.log(map.get(xiaoming)) // { region: 'HN' } // 獲取不存在的 key 得到 undefined console.log(map.get(lihua)) // undefined // 通過 has 函數(shù)判斷指定 key 是否存在 console.log(map.has(lihua)) // false console.log(map.has(xiaoming)) // true // map存儲映射個數(shù) console.log(map.size) // 2 // delete 刪除 key map.delete(xiaoming) console.log(map.has(xiaoming)) // false console.log(map.size) // 1 // clear 清空 map map.clear() console.log(map.size) // 0
2、遍歷 Map
Map 可以確保遍歷的順序和插入的順序一致
const zhangsan = { name: 'zhangsan' } const xiaoming = { name: 'xiaoming' } const map = new Map() map.set(zhangsan, { region: 'HB' }) map.set(xiaoming, { region: 'HN' }) // 每個鍵值對返回的是 [key, value] 的數(shù)組 for (let item of map) { // = for (let item of map.entries()) { console.log(item) // [ { name: 'zhangsan' }, { region: 'HB' } ] // [ { name: 'xiaoming' }, { region: 'HN' } ] } // 遍歷 key for (let key of map.keys()) { console.log(key) // { name: 'zhangsan' } // { name: 'xiaoming' } } // 遍歷 value for (let key of map.values()) { console.log(key) // { region: 'HB' } // { region: 'HN' } } // 使用 forEach() 方法迭代 Map map.forEach(function(value, key) { console.log(key, value) // { name: 'zhangsan' } { region: 'HB' } // { name: 'xiaoming' } { region: 'HN' } })
3、Map 中判斷 key 相等
Map
內(nèi)部使用 SameValueZero
比較操作。
關(guān)于SameValue
和 SameValueZero
SameValue (Object.is()
) 和嚴格相等(===)相比,對于 NaN 和 +0,-0 的處理不同
Object.is(NaN, NaN) // true Object.is(0, -0) // false
SameValueZero
與 SameValue
的區(qū)別主要在于 0 與 -0 是否相等。
map.set(NaN, 0) map.set(0, 0) console.log(map.has(NaN)) // true console.log(map.has(-0)) // true
4、復(fù)制或合并 Map
Map 能像數(shù)組一樣被復(fù)制
let original = new Map([ [1, {}] ]) let clone = new Map(original) // 克隆 Map console.log(clone.get(1)); // {} console.log(original === clone) // false console.log(original.get(1) === clone.get(1)) // true
多個 Map 合并
let first = new Map([ [1, 'one'], [2, 'two'], [3, 'three'], ]); let second = new Map([ [1, 'uno'], [2, 'dos'] ]); // 合并兩個 Map 對象時,如果有重復(fù)的鍵值,則后面的會覆蓋前面的。 // 展開運算符本質(zhì)上是將 Map 對象轉(zhuǎn)換成數(shù)組。 let merged = new Map([...first, ...second]); console.log(merged.get(1)); // uno console.log(merged.get(2)); // dos console.log(merged.get(3)); // three
5、Map 序列化
Map
無法被序列化,如果試圖用 JSON.stringify
獲得 Map 的 JSON 的話,只會得到 "{}"。
由于 Map 的鍵可以是任意數(shù)據(jù)類型,而 JSON 僅允許將字符串作為鍵,所以一般情況下無法將 Map 轉(zhuǎn)為 JSON。
不過可以通過下面的方式去嘗試序列化一個 Map:
// 初始化 Map(1) {"key1" => "val1"} const originMap = new Map([['key1', 'val1']]) // 序列化 "[[\"key1\",\"val1\"]]" const mapStr = JSON.stringify(Array.from(originMap.entries())) // 反序列化 Map(1) {"key1" => "val1"} const cloneMap = new Map(JSON.parse(mapStr))
三、Map 和 Object 的性能差異
內(nèi)存占用
不同瀏覽器的情況不同,但給定固定大小的內(nèi)存,Map
大約可以比 Object
多存儲 50% 的鍵/值對。
插入性能
Map 略快,如果涉及大量操作,建議使用 Map
。
查找速度
性能差異極小,但如果只包含少量鍵/值對,則 Object
有時候速度更快。Object
作為數(shù)組使用時瀏覽器會進行優(yōu)化。如果涉及大量查找操作,選擇 Object 會更好一些。
刪除性能
如果代碼涉及大量的刪除操作,建議選擇 Map
。
到此這篇關(guān)于JavaScript
中有了Object
為什么還需要 Map 呢的文章就介紹到這了,更多相關(guān)JavaScript Map
內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript?API調(diào)用Rollup打包流程快速上手
這篇文章主要為大家介紹了JavaScript?API調(diào)用Rollup打包流程快速上手2023-05-05解析JS參數(shù)parseInt('012',?16)和parseInt(012,?16)是否相等
這篇文章主要為大家介紹了parseInt('012',?16)和parseInt(012,?16)是否相等原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02