詳解?Map?和?WeakMap?區(qū)別以及使用場(chǎng)景
一、為什么是 Map ?
1. 傳統(tǒng)對(duì)象結(jié)構(gòu)
Map本質(zhì)上是一個(gè)鍵值對(duì)的集合。和傳統(tǒng)對(duì)象結(jié)構(gòu)相比,傳統(tǒng)的對(duì)象只能用「字符串作為鍵名」,這就在使用上造成了很大的限制了。這也是新增 Map 的原因之一。
const data = {}; // element 為節(jié)點(diǎn)對(duì)象 const element = document.querySelector(".node"); console.log(element); // 輸出 div.node 對(duì)象 // 將對(duì)象轉(zhuǎn)化成字符串輸出 [object HTMLDivElement] console.log(element.toString());? // 用點(diǎn)操作符不能有空格,所以采用中括號(hào)的形式給對(duì)象賦值 data[element] = 'objectData' // 輸出 objectData,說(shuō)明在對(duì)象中存在[object HTMLDivElement]鍵名 console.log(data['[object HTMLDivElement]']);
在上面的代碼中,我們創(chuàng)建了一個(gè)對(duì)象并將一個(gè)節(jié)點(diǎn)對(duì)象作為了它的鍵名,并進(jìn)行了代碼測(cè)試,首先驗(yàn)證了獲取到的element
節(jié)點(diǎn)為一個(gè)對(duì)象,再確定了經(jīng)過(guò)toString
方法轉(zhuǎn)化后的結(jié)果,以這個(gè)值為鍵名成功的輸出了value
值objectData
通過(guò)上面的測(cè)試,確定了傳統(tǒng)對(duì)象的鍵名會(huì)通過(guò)toString
方法轉(zhuǎn)化為「字符串類(lèi)型」
注意:在我們?cè)L問(wèn)對(duì)象成員時(shí),鍵名「有空格」時(shí)不能采用點(diǎn)訪(fǎng)問(wèn),例如data.ab c
這樣是「錯(cuò)誤的」。我們需要采用data['ab c']的形式來(lái)訪(fǎng)問(wèn)
2. Map 結(jié)構(gòu)
Map
類(lèi)似于對(duì)象,但是鍵名不限于字符串,可以說(shuō)Object
結(jié)構(gòu)提供鍵-值對(duì)應(yīng),Map結(jié)構(gòu)提供值-值對(duì)應(yīng)因此其實(shí)采用map結(jié)構(gòu)會(huì)優(yōu)于傳統(tǒng)對(duì)象
// 1. 通過(guò)new Map來(lái)創(chuàng)建dataMap容器 const dataMap = newMap(); // 2. 獲取節(jié)點(diǎn)對(duì)象,作為測(cè)試數(shù)據(jù) const element = document.querySelector(".node"); // 3. 通過(guò) set 方法給 dataMap 中指定鍵和對(duì)應(yīng)的值 dataMap.set(element,'objectData'); // 4. 通過(guò) get 來(lái)從 dataMap 中獲取鍵名對(duì)應(yīng)的值 console.log(dataMap.get(element)); // 5. 揭開(kāi)面目 console.log(dataMap);
從上面的代碼中,我們可以清楚的看到,第8行代碼獲取值時(shí)直接傳入了element
對(duì)象,
可以成功的獲取到對(duì)應(yīng)的值,在最后打印dataMap
時(shí)更是驗(yàn)證了上訴說(shuō)法
成功的將對(duì)象作為了鍵名,彌補(bǔ)了傳統(tǒng)對(duì)象的不足
3. Map 的特點(diǎn)
- Map 默認(rèn)情況下不包含任何鍵,所有鍵都是自己添加進(jìn)去的。不同于
Object
原型鏈上有一些默認(rèn)的鍵。 - Map 的鍵可以是「任意類(lèi)型」數(shù)據(jù),就連函數(shù)都可以。
- Map 的鍵值對(duì)個(gè)數(shù)可以「輕易」通過(guò)
size
屬性獲取,Object
需要手動(dòng)計(jì)算。 - Map 在頻繁增刪鍵值對(duì)的場(chǎng)景下「性能」要比
Object
好。
4. 什么時(shí)候用 Map
要添加的鍵值名和 Object
上的默認(rèn)鍵值名沖突,又不想改名時(shí),「用 Map」
需要 String
和 Symbol
以外的數(shù)據(jù)類(lèi)型做鍵值時(shí),「用 Map」
鍵值對(duì)很多,有需要計(jì)算數(shù)量時(shí),「用 Map」
需要頻繁增刪鍵值對(duì)時(shí),「用 Map」
二、Map 實(shí)例屬性和方法
在上面我們已經(jīng)接觸到了Map
的個(gè)別 API,接下來(lái)簡(jiǎn)單說(shuō)說(shuō)
1. set
set方法設(shè)置鍵名key對(duì)應(yīng)的鍵值為value
,然后會(huì)返回整個(gè)Map
結(jié)構(gòu),如果設(shè)置的key已經(jīng)存在,則會(huì)更新value
值,否則會(huì)新生成該鍵
2. get
通過(guò)get方法讀取key對(duì)應(yīng)的鍵值,如果傳入的鍵值不存在,則會(huì)返回undefined
控制臺(tái)成功輸出ljc
3. has
判斷傳入的鍵是否存在當(dāng)前Map
對(duì)象中,該方法返回一個(gè)布爾值
在上面的代碼中,存在name
為true
,不存在sex返回false
4. delete
刪除傳入的鍵,返回true
,如果刪除失敗,則返回false
5. clear
三、遍歷方法
可以采用for...of循環(huán)和forEach
兩種方法。由于Map實(shí)例會(huì)維護(hù)鍵值對(duì)的插入順序,因此可以根據(jù)插入順序進(jìn)行遍歷
采用「for...of」
for...of可以遍歷有iterator接口的數(shù)據(jù)結(jié)構(gòu)
- keys():返回鍵名的遍歷器
- values():返回鍵值的遍歷器
- entries():返回鍵值對(duì)的遍歷器
- forEach():使用回調(diào)函數(shù)遍歷每個(gè)成員
map.entries()
在Map實(shí)例中「有一個(gè)迭代器」,能以插入順序生成[key,value]
形式的數(shù)據(jù)。
也可以采用如下進(jìn)行遍歷,每次item
獲取到一個(gè)數(shù)組
通過(guò)回調(diào)的方式遍歷map
四、Map 類(lèi)型轉(zhuǎn)化
幾種與map相互類(lèi)型轉(zhuǎn)化的方法
Map 轉(zhuǎn)為數(shù)組
通過(guò)擴(kuò)展運(yùn)算符實(shí)現(xiàn)
let map = newMap() let arr = [...map]
數(shù)組轉(zhuǎn)為 Map
let map = newMap(arr)
Map 轉(zhuǎn)為對(duì)象
通過(guò)遍歷利用set
將鍵值對(duì)加入對(duì)象中
let obj = {} for (let [k, v] of map) { ? obj[k] = v } 對(duì)象轉(zhuǎn)為 Map for( let k ofObject.keys(obj)){ ? map.set(k,obj[k]) }
五、什么是 WeakMap ?
總所周知,WeakMap
是 ES6 中新增的一種集合類(lèi)型,叫做“弱映射”。它和Map是兄弟關(guān)系,與Map的區(qū)別就在于這個(gè)「弱字」,API 還是Map的那套(只有set get has delete)
那它真正是什么意思呢?
這其實(shí)描述的是 JS 中「垃圾回收」程序?qū)Υ?ldquo;弱映射”中鍵的方式
那為什么要有 WeakMap 呢?它解決了什么問(wèn)題呢?這些問(wèn)題后面都會(huì)講到
六、WeakMap 的特性
我們先從 WeakMap 的特性講起
1. WeakMap 只能將對(duì)象作為鍵名
只接受對(duì)象作為鍵名(null 除外),不接受其他類(lèi)型的值作為鍵名
「null 除外」
2. WeakMap 的鍵名引用的對(duì)象是弱引用
這里懵了挺久的,但是這是WeakMap結(jié)構(gòu)的關(guān)鍵所在
要想讀懂這句話(huà),不容易,我們需要先知道「強(qiáng)引用和弱引用」
2.1 什么是強(qiáng)引用?
我們先來(lái)看看「強(qiáng)引用」,這是阮一峰老師書(shū)上的例子
「麻煩的操作勢(shì)必會(huì)造成問(wèn)題,當(dāng)忘記了手動(dòng)刪除引用,就會(huì)造成內(nèi)存泄漏」
2.2 什么是弱引用?
對(duì)于「弱引用」,百度百科給出的答案:
在計(jì)算機(jī)程序設(shè)計(jì)中,弱引用與強(qiáng)引用相對(duì),是指不能確保其引用的對(duì)象不會(huì)被垃圾回收器回收的引用。一個(gè)對(duì)象若只被弱引用所引用,則被認(rèn)為是不可訪(fǎng)問(wèn)(或弱可訪(fǎng)問(wèn))的,并因此可能在任何時(shí)刻被回收。
也就是說(shuō)「如果」我們能這樣創(chuàng)建一個(gè)弱引用的對(duì)象
//假設(shè) let obj = new WeakObject()
我們就可以靜靜的等待垃圾車(chē)來(lái)把它拖走了,obj所引用的對(duì)象就會(huì)被回收
如果還沒(méi)有理解的話(huà),我們?cè)賮?lái)看看
2.3 弱引用和強(qiáng)引用圖解
從1套代碼結(jié)合兩張圖來(lái)理解
對(duì)于強(qiáng)引用:
const myMap = newMap() let my = { ? ? name: "ljc", ? ? sex: "男" } myMap.set(my, 'info'); console.log(myMap);
對(duì)于弱引用
const myMap = newWeakMap() let my = { ? ? name: "ljc", ? ? sex: "男" } myMap.set(my, 'info'); console.log(myMap);
圖一中的數(shù)據(jù)被my和myMap實(shí)例對(duì)象所引用,引用計(jì)數(shù)為 2,圖2中建立了myMap對(duì)my所引用的對(duì)象的「弱引用」,引用計(jì)數(shù)為 1
在上面我們談到強(qiáng)引用數(shù)據(jù)被刪除時(shí),需要手動(dòng)解除引用,而弱引用則可以等待垃圾回收機(jī)制自動(dòng)清除
「弱引用與垃圾回收」
當(dāng)執(zhí)行my = null時(shí)會(huì)解除my對(duì)原數(shù)據(jù)的引用,而myMap實(shí)例對(duì)象對(duì)my所引用對(duì)象是弱引用關(guān)系,該數(shù)據(jù)的「引用計(jì)數(shù)為 0」 ,程序垃圾回收機(jī)制在執(zhí)行時(shí)會(huì)將引用對(duì)象回收。而如果時(shí)強(qiáng)引用關(guān)系則「引用計(jì)數(shù)為 1」 ,不會(huì)被垃圾回收機(jī)制清除。
總的來(lái)說(shuō), WeakMap 保持了對(duì)鍵名所引用的對(duì)象的弱引用,即垃圾回收機(jī)制不將該引用考慮在內(nèi)。只要所引用的對(duì)象的其他引用都被清除,垃圾回收機(jī)制就會(huì)釋放該對(duì)象所占用的內(nèi)存。也就是說(shuō),一旦不再需要,WeakMap 里面的鍵名對(duì)象和所對(duì)應(yīng)的鍵值對(duì)會(huì)自動(dòng)消失,不用手動(dòng)刪除引用。
3. 不可遍歷
正因?yàn)閃eakMap對(duì)鍵名所引用的對(duì)象是弱引用關(guān)系,因此WeakMap內(nèi)部成員是會(huì)「卻決于垃圾回收機(jī)制有沒(méi)有執(zhí)行」,運(yùn)行前后成員個(gè)數(shù)很可能是不一樣的,而垃圾回收機(jī)制的執(zhí)行又是「不可預(yù)測(cè)」的,因此不可遍歷
了解了WeakMap的特性,相信對(duì)“為什么要有WeakMap?”已經(jīng)有了一定的答案
七、Map 和 WeakMap 的區(qū)別
看到這里相信心中已經(jīng)有答案了
- Map 的鍵可以是任意類(lèi)型,WeakMap 只接受對(duì)象作為鍵(null除外),不接受其他類(lèi)型的值作為鍵
- Map 的鍵實(shí)際上是跟內(nèi)存地址綁定的,只要內(nèi)存地址不一樣,就視為兩個(gè)鍵;WeakMap 的鍵是弱引用,鍵所指向的對(duì)象可以被垃圾回收,此時(shí)鍵是無(wú)效的
- Map 可以被遍歷, WeakMap 不能被遍歷
八、WeakMap 的使用場(chǎng)景
1. DOM 節(jié)點(diǎn)元數(shù)據(jù)
用紅寶書(shū)的例子
因此可以采用WeakMap當(dāng)節(jié)點(diǎn)刪除后,引用計(jì)數(shù)為0,等待垃圾回收機(jī)制回收
2. 部署私有屬性
利用弱映射,將內(nèi)部屬性設(shè)置為實(shí)例的弱引用對(duì)象,當(dāng)實(shí)例刪除時(shí),私有屬性也會(huì)隨之消失,因此不會(huì)內(nèi)存泄漏
阮一峰老師的代碼實(shí)例
3. 數(shù)據(jù)緩存
當(dāng)我們需要在不修改原有對(duì)象的情況下儲(chǔ)存某些屬性等,而又不想管理這些數(shù)據(jù)時(shí),可以使用WeakMap
到此這篇關(guān)于詳解 Map 和 WeakMap 區(qū)別以及使用場(chǎng)景的文章就介紹到這了,更多相關(guān)Map 和 WeakMap 區(qū)別以及使用場(chǎng)景內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!到此這篇關(guān)于詳解 Map 和 WeakMap 區(qū)別以及使用場(chǎng)景的文章就介紹到這了,更多相關(guān)Map 和 WeakMap 區(qū)別以及使用場(chǎng)景內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
原生JavaScript寫(xiě)出Tabs標(biāo)簽頁(yè)的實(shí)例代碼
這篇文章主要介紹了原生JavaScript寫(xiě)出Tabs標(biāo)簽頁(yè)的實(shí)例代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07利用JavaScript實(shí)現(xiàn)網(wǎng)頁(yè)版2048小游戲
這篇文章主要介紹了如何利用HTML+CSS+JS編寫(xiě)一個(gè)網(wǎng)頁(yè)版的2048小游戲,代碼簡(jiǎn)單易懂對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-11-11在Postman的腳本中如何使用pm對(duì)象獲取接口的請(qǐng)求參數(shù)
這篇文章主要介紹了在Postman的腳本中如何使用pm對(duì)象獲取接口的請(qǐng)求參數(shù),本文通過(guò)實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09