一路上感謝各位讀者們的支持和回饋。
本 30 天系列文目前已經將篇幅重新整理、編纂成冊。
《JavaScript 概念三明治》在天瓏書局上架囉!
喜歡這個系列,想閱讀更詳細原理說明的讀者可以參考:
https://www.tenlong.com.tw/products/9789864347575
ES6 之後加入兩種新的資料結構:Map 跟 Set 。 Map 與 Set 都是像字串跟陣列這樣可以被尋訪的類型,也就是說可以使用 for 迴圈去一個一個查找跟操作他們的值。今天就來說明一下這兩個類別跟使用方式吧!
Set 的中文翻譯與數學裡面的「集合」相同,「集合」是某個定義好並且具有相同性質的元素的集合,講白話一點就是「一堆東西」。在 JS 內的集合當然代表「一堆值」,他跟陣列有點像,差別在 Set 能夠讓開發者可以方便快速的儲存不重複、獨特的數值。至於 Set
內儲存的元素內容沒有型別限制,可以是純值也可以是物件型別。
Set
除了具有儲存不重複數值的性質外,在上面還有一些很方便的方法可以直接處理數值,讓我們陸續來看看,首先創造一個新的 Set
,創造新的 Set
很簡單,只要在 Set
的建構子傳入一個陣列即可:
let set = new Set([1,2,3,'Hello','World',true])
在 Set 類別上有許多方法讓我們可以用比較語意化的方式操作 Set 內容:
Set
內Set
內的元素Set
內特定的某個元素forEach
功能相同Set
內有沒有對應值的元素,這個功能如果在陣列內,必須透過 indexOf
來檢查才能達成。Set
元素長度就像前面說過的, Set
內儲存的是不重複的元素,因此如果有相同數值的元素再次被傳入,這個數值就會直接被忽略。
set.size //6
set.add('Hello')
set.size //6
對 Set 做巡訪的方式跟陣列很相似,一樣可以用 forEach
方法,甚至 Set
可以很方便的直接轉為陣列 :
let setArr = [...set]
這個特性非常好用,利用這點我們就可以很快速的過濾出陣列內的重複值!
let duplicatedValueArr = [1,2,3,5,10,19,10,4,5,6,3,1,2]
let uniqueArr = [...new Set(duplicatedValueArr)]
這樣子是不是既方便快速又簡潔? 如果單純使用陣列可能還需要透過 filter
跟外部變數來儲存重複值輔助檢查,使用 Set
的話,這些功夫都可以省去。
Map
也是跟陣列、跟 Set
具有相同特性且可被巡訪的物件型別,差別在於, Map
跟物件ㄧ樣是鍵值的組合,也就是說,Map
同時具有跟陣列ㄧ樣可以被巡訪的特色,同時也有物件儲存任意屬性跟數值的能力。
Map
類型上的方法也與 Set
大同小異,差別在 Set 新增元素的方法是使用 add
,而 Map
內必須用 set
方法 ,且新增元素時必須傳入兩個參數,第一個是要儲存的鍵 ( key ),另外一個是要儲存的數值內容 ( value )。
創造新的 Map
的方式與創造 Set
相同,但由於 Map
是鍵-值對的結構,傳入建構子內的陣列內不能夠像 Set 那樣只是個單一元素,而必須要是個鍵-值的組合,所以我們可以用二維陣列來達成,大概像是這樣:
let map = new Map([['name','Luke'],['Hello','World']])
取得 Map 元素 :
map.get('name') // Luke
新增元素 :
map.set('Greeting','I am Anakin') // { ... 'Hello'=>'World', 'Greeting'=> 'I am Anakin'}
其他像是刪除特定元素或是刪除所有 Map 內元素則都跟 Set 上的方法差不多:
map.delete('Hello')
map.clear()
map.size
Map 其實跟物件ㄧ樣都是 鍵-值 的組合,事實上這些結構相似的類型有許多種,如,那麼使用 Map 相比於使用物件有什麼好處呢?還記得前面提到在 JS 內除了原始型別以外的型別都是物件型別嗎?這代表除了物件以外像是 Array
以及Function
這樣的型別都是繼承自 Object
,這其中當然包含 Map
。
所以這兩種型別才有這麼相似的結構 ,性質相同的部分就不用多說了,但是這兩者還是有一些不差異,這些差異可能足以影響資料存取的複雜度以及程式碼閱讀的難易度,所以我們可以認識一下究竟兩者有什麼不同的地方:
鍵值的類型:
在物件內的鍵值(或屬性名稱) 必須是字串或是 Symbol
。而在 Map
內,鍵值可以是任何型別,這包含任何其他的物件或是陣列 。你當然可以試試看用物件來當作另外一個物件的屬性名稱,不過這個物件會被 JS 強制轉型變成 [object Object]
而變成另外一個字串屬性。
let o = {}
let anotherObj = {}
o[anotherObj] = 'anotherObject' // {'[object Object]' : anotherObject}
let theThirdObj = {}
o [theThirdObj] = 'theThirdObj' // {'[object Object]' : anotherObject}
元素的順序,在 Map
裡面,元素被新增進去之後,順序就會被固定下來。而在 Object 內則無法保證。
繼承關係:Map
繼承於物件 ( Object ) ,而反過來則否,因此在 Map
上那些方便的方法,在 Object 上無法使用。
let newMap = new Map()
console.log(newMap instanceof Object) //true
console.log(Object instanceof newMap) //false
可被巡訪:這大概是最大的差別了,因為一般物件上並沒有提供可以直接巡訪的方法,只能透過 for .. in
迴圈達成,或是必須透過 Object.keys
方法把屬性轉為陣列,但是在陣列 、 Set 跟 Map 上都有 forEach
方法可以直接對裡面的元素做巡訪。
Map
在操作元素上雖然提供了許多語意化的方法,但有時候我們還是會需要像一般物件那樣方便新增元素的方式,最後我們就來看看兩者各適合怎樣的使用情境:
get
、 set
函式幫忙,因此速度上會比單純使用物件還要慢。Map
內的元素順序會被保留,因此在處理資料時,如果維持順序的穩定很重要,就可以考慮使用 Map
。Map
就比較麻煩。除了前面我們提到的幾個基本資料結構,今天我們又認識了 JS 內新的 Map 跟 Set 兩種新的資料型別。在資料結構選擇上永遠是根據你的需求而定,雖然用簡單的物件或陣列組合或許就可以達到,多認識一些這樣子的資料結構不一定會大幅度增加開發速度,但絕對會讓你在開發時有更多其他潛在更好的選擇來達成你的需求。