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