Set 和 Map 數據結構
Set
Set 對象允許你存儲任何類型的 唯一值, 無論是 原始值(一共6種,string, number, boolean, undefined, null,和 es6 新增的 symbol) 還是 對象引用(Object)。
先用代碼體會下這段概念的含義,直接在 Chrome 控制臺創建一個 Set 數據類型,並因此向 Set 的實例中添加 原始值 和 對象引用,查看最終的 Set 數據機構和成員個數
上圖中,我們依次在 Set 數據結構中存儲了5種常見的原始數據類型和2種Object數據類型,最終全部存儲成功,並且查看到有7個成員
1、語法:
new Set([iterable]);
參數:iterable
可以傳入一個 可叠代對象 用來初始化 Set 數據類型,它的所有元素將被添加到新的 Set 中。如果不指定此參數或者其值為 null,則新的Set 為空。
(可叠代對象包括:Array, String, Set, Map, TypedArray, 函數的 arguments 對象,NodeList 對象等)
返回值:
一個新的 Set 對象
我們現在依次在 Chrome 控制臺中用 可叠代對象 和 普通原始數據類型(String 屬於可叠代對象)來初始化 Set 數據類型
再來看一個 NodeList 作為參數來初始化 Set 數據類型的小栗子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div>11</div> <div>22</div> <div>33</div> <script> var divs = document.querySelectorAll(‘div‘); var set_NodeList = new Set(divs); console.log(set_NodeList); </script> </body> </html>
輸出結果:
2、概括:
(1)、Set 中的元素是唯一的
let arr=[1,2,3,1,‘2‘]; let list2=new Set(arr); console.log(‘unique‘,list2); // Set(4) {1, 2, 3, "2"}
(2)、向 Set 加入值的時候,不會發生類型轉換,所以 5 和 "5" 是兩個不同的值
let s6 = new Set(); s6.add(5); s6.add(6); s6.add(‘5‘); s6.add(6); console.log(s6); // Set(3) {5, 6, "5"}
(3)、NaN, undefined 和 null 都可以被存儲在 Set 中,在 Set 內部, 兩個 NaN 是相等的(盡管在 es5 中 NaN != NaN)
(4)、兩個對象總是不相等的(包括空對象)
let s8 = new Set(); s8.add({}); s8.add({}); console.log(s8); // Set(2) {Object {}, Object {}} s8.add({‘name‘: ‘roger‘}); s8.add({‘age‘: 27}); console.log(s8); // Set(4) {Object {}, Object {}, Object {name: "roger"}, Object {age: 27}}
(5)、數組去重 [ ...new Set( array ) ]
let arr1 = [‘apple‘, ‘huawei‘, ‘mi‘, ‘oppo‘, ‘apple‘]; let arr2 = [...new Set(arr1)]; console.log(arr2); // ["apple", "huawei", "mi", "oppo"]
(6)、Array.from 方法可將 Set 結構轉為數組
let s9 = new Set(); s9.add([1,2,3]); let arr = Array.from(s9); console.log(Array.isArray(arr)); // true // 這就提供了去除數組重復成員的另一種方法 function dedupe(array) { return Array.from(new Set(array)); } dedupe([1, 1, 2, 3]); // [1, 2, 3]
3、遍歷操作
Set 實例的方法分為兩大類:操作方法(用於操作數據)和遍歷方法(用於遍歷成員)
(1)、四個操作方法
add (value):添加某個值,返回 Set 結構本身
delete(value):刪除某個值,返回一個 bool 值,表示是否刪除成功
clear(value):清除所有成員,沒有返回值
has(value):返回一個 bool 值,表示該值是否是 Set 的成員
另外,size 相當於數組的 length 屬性,表示 Set 成員的個數
let list = new Set (); list.add(5); list.add(7); console.log(‘size‘,list.size); // size 2 // 數組作為參數 let arr = [1,2,3,4,5]; let list1 = new Set (arr); console.log(‘size‘,list1.size); // size 5 // 數組去重 let list2 = new Set(); list2.add(2); list2.add(3); list2.add(3); console.log(‘list2‘,list2); // Set(2) {2, 3} let arr3=[1,2,3,1,‘2‘]; let list3=new Set(arr3); console.log(‘unique‘,list3); // Set(4) {1, 2, 3, "2"}
let arr4 = [‘add‘, ‘delete‘, ‘has‘, ‘clear‘]; let list4 = new Set (arr4); console.log(‘has‘, list4.has(‘add‘)); // has true console.log(‘delete‘, list4.delete(‘add‘), list4); // delete true Set(3) {"delete", "has", "clear"} console.log(‘clear‘, list4.clear(), list4); // clear undefined Set(0) {}
(2)、四個遍歷方法
keys():返回鍵名
values():返回鍵值
entries():返回鍵值對
forEach():使用回調函數遍歷每個成員
let arr7 = [‘one‘, ‘two‘, ‘three‘, ‘four‘, ‘five‘]; let list7 = new Set(arr7); for (let key of list7.keys()){ console.log(‘keys‘,key); } // keys one // keys two // keys three // keys four // keys five for(let value of list7.values()){ console.log(‘values‘,value); } // values one // values two // values three // values four // values five for(let entries of list7.entries()){ console.log(‘entries‘, entries) } entries (2) ["one", "one"] entries (2) ["two", "two"] entries (2) ["three", "three"] entries (2) ["four", "four"] entries (2) ["five", "five"] list7.forEach(function(item){ console.log(item) }) // one // two // three // four // five
WeakSet
WeakSet 對象 與 Set 類似,也是不重復的值的集合
它與 Set 對象的區別有兩點:
? WeakSet 的成員 只能是對象,不能是其它類型的值。而 Set 對象都可以;並且,WeakSet 沒有 size 屬性
? WeakSet 中的對象都是弱引用,如果沒有其它變量或屬性引用這個對象值,那麽這個對象值會被當垃圾回收掉。正因如此,WeakSet 對象是無法遍歷的,沒有辦法拿到它所包含的元素
1、語法:
new WeakSet([iterable]);
參數:iterable
如果傳入一個 可叠代對象, 則該對象所有的 叠代值 都會被自動添加進生成的 WeakSet 對象中
註意:初始化 WeakSet 的參數是有2個要求的
(1)、傳入的參數必須是一個 可叠代對象
(2)、可叠代對象的值必須是 叠代值
通過一個例子來理解這段話:
2、方法
WeakSet.prototype.add(value):向 WeakSet 實例添加一個新成員。
WeakSet.prototype.delete(value):清除 WeakSet 實例的指定成員。
WeakSet.prototype.has(value):返回一個布爾值,表示某個值是否在 WeakSet 實例之中。
Map
Map 對象保存鍵值對。任何值(包括原始值和對象)都可以作為一個鍵或值
1、語法
new Map([iterable])
參數:iterable
與 WeakSet 類似,初始化Map時,Map 的參數必須是 iterable 對象,該對象的值也必須為 iterable值
JavaScript 的對象,本質是鍵值對的集合(Hash 結構),但是傳統上只能用字符串當鍵,這給它的使用帶來了很大的限制。
例如,在《JavaScript高級程序設計》一書中,關於 Object 類型的介紹中,有這麽一段話 “ 如果屬性名中包含會導致語法錯誤的字符,或者屬性名使用的是關鍵字或保留字,也可以使用方括號表示法。例如:”
person["first name"] = "Nicholas";
“ 由於 ‘first name’ 中包含一個空格,所以不能用點表示法來訪問它。然而,屬性名中是可以包含非字母非數字的,這時候就可以使用方括號表示法來訪問它們。 ”
現在有一個如下結構的對象,該如何給這樣一個對象添加和讀取新成員呢?
{ map:contra : {description: ‘Asynchronous flow control‘},
map:dragula: {description: ‘Drag and drop‘},
map:woofmark: {description: ‘Markdown and WYSIWYG editor‘} }
我們可以按照如下方法來實現:
var obj = {} // 給對象添加成員 function add(name, meta){ obj[‘map:‘+name] = meta } // 讀取對象成員 function get(name){ return obj[‘map:‘+name] } add(‘contra‘, { description: ‘Asynchronous flow control‘ }) add(‘dragula‘, { description: ‘Drag and drop‘ }) add(‘woofmark‘, { description: ‘Markdown and WYSIWYG editor‘ }) get(‘contra‘)
但如果使用 Map 結構來實現的話,就可以簡單很多
var map = new Map() map.set(‘contra‘, { description: ‘Asynchronous flow control‘ }) map.set(‘dragula‘, { description: ‘Drag and drop‘ }) map.set(‘woofmark‘, { description: ‘Markdown and WYSIWYG editor‘ }) console.log(map)
結果如下:
通過前面的例子,我們來進一步了解下Map 和 Object 之間的相似點和不同點
相似點:
? 都是鍵值對的集合(Hash 結構)
? 允許按鍵 添加、刪除一個值
? 可以刪除鍵
? 可以檢測一個鍵是否綁定了值
不同點:
? Object 的鍵只能是 字符串 或 Symbol,Map 的鍵可以是任意值
? 可以通過 size 屬性獲取 Map 的鍵值對個數,而 Object 的鍵值對個數只能手動確認
? Object 都有自己的原型 prototype,不過,從 ES5 開始,可以通過 “ map = Object.create( null ) ” 來創建一個沒有原型的對象
2、Map 實例的屬性和方法
(1)、set(key, value)
set 方法設置鍵名 key 對應的鍵值 value,然後返回整個 Map 結構。如果 key 已經有值,則鍵值會被更新,否則就新生成該鍵
const m = new Map(); m.set(‘edition‘, 6) // 鍵是字符串 Map(1) {"edition" => 6} m.set(262, ‘standard‘) // 鍵是數值 Map(2) {"edition" => 6, 262 => "standard"} m.set(undefined, ‘nah‘) // 鍵是 undefined Map(3) {"edition" => 6, 262 => "standard", undefined => "nah"}
m.set([‘123‘], 456) // 鍵是數組 Map(4) {"edition" => 6, 262 => "standard", undefined => "nah", ["123"] => 456}
set 方法返回的是當前的 Map 對象,因此可以采用鏈式寫法
let map = new Map() .set(1, ‘a‘) .set(2, ‘b‘) .set(3, ‘c‘);
// Map(3) {1 => "a", 2 => "b", 3 => "c"}
(2)、get(key)
get 方法讀取 key 對應的鍵值,如果找不到 key,返回 undefined
const m = new Map(); const hello = function() {console.log(‘hello‘);}; m.set(hello, ‘Hello ES6!‘) // 鍵是函數 m.get(hello) // Hello ES6! m.get(‘world‘) // undefined
(3)、size 屬性
size 屬性返回 Map 結構的成員總數
const map = new Map() map.set(‘foo‘, true) map.set(‘bar‘, false) console.log(map.size) // 2
(4)、has(key)
has 方法返回一個 bool 值,表示某個鍵是否在當前的 Map 對象中
const m = new Map(); m.set(‘edition‘, 6); m.set(262, ‘standard‘); m.set(undefined, ‘nah‘); m.has(‘edition‘) // true m.has(‘years‘) // false m.has(262) // true m.has(undefined) // true
(5)、delete(key)
delete 方法刪除某個鍵,返回 true。如果刪除失敗,返回 false
const m = new Map(); m.set(undefined, ‘nah‘); m.has(undefined) // true m.delete(undefined) m.has(undefined) // false
(6)、clear()
clear 方法清除所有成員,沒有返回值
let map = new Map(); map.set(‘foo‘, true); map.set(‘bar‘, false); map.size // 2 map.clear() map.size // 0
3、遍歷方法
keys():返回鍵名的遍歷器
values():返回鍵值的遍歷器
entries():返回所有成員的遍歷器
forEach():返回Map的所有成員
也可以使用 forEach() 方法遍歷
var myMap = new Map(); myMap.set(0, ‘Zero‘); myMap.set(1, ‘One‘); myMap.forEach(function(value, key){ console.log(key+‘=‘+value) }, myMap) // 0=Zero // 1=One
4、與其它數據結構的互相轉換
(1)、Map 轉為數組,使用擴展運算符(...)
var map = new Map(); map.set({‘hello‘: ‘world‘}) .set([1, NaN, ‘china‘]); var map_array = [...map]; console.log(map_array); // [[{hello: "world"}, undefined], [[1, NaN, "china"], undefined]
(2)、數組轉為Map,將數組傳入Map構造函數
var map = new Map([[‘hello‘, null],[‘world‘, undefined]]); // Map(2) {"hello" => null, "world" => undefined}
WeakMap
WeakMap 對象是一組鍵值對的集合,且其中的鍵是弱引用的。鍵必須是對象,值可以是任意值
1、語法
new WeakMap([iterable])
參數:iterable
iterable 是一個2元數組或者可遍歷的且其元素是鍵值對的對象
var mp = new WeakMap(); mp.set({‘age‘: 27}, 2017); // WeakMap {{age: 27} => 2017} mp.set(1,2); // Uncaught TypeError: Invalid value used as weak map key
2、註意點:
(1)、WeakMap只接受對象作為鍵名(null除外),不接受其他類型的
(2)、WeakMap的鍵名所指向的對象,不計入垃圾回收機制
(3)、WeakMap 沒有遍歷操作,無法清空,也沒有 size 屬性
3、方法
WeakMap只有四個方法可用:get()、set()、has()、delete()
var wm1 = new WeakMap(), wm2 = new WeakMap(), wm3 = new WeakMap(); var o1 = {}, o2 = function(){}, o3 = window; wm1.set(o1, 37); wm1.set(o2, "azerty"); wm2.set(o1, o2); // value可以是任意值,包括一個對象 wm2.set(o3, undefined); wm2.set(wm1, wm2); // 鍵和值可以是任意對象,甚至另外一個WeakMap對象 wm1.get(o2); // "azerty" wm2.get(o2); // undefined,wm2中沒有o2這個鍵 wm2.get(o3); // undefined,值就是undefined wm1.has(o2); // true wm2.has(o2); // false wm2.has(o3); // true (即使值是undefined) wm3.set(o1, 37); wm3.get(o1); // 37 wm3.clear(); wm3.get(o1); // undefined,wm3已被清空 wm1.has(o1); // true wm1.delete(o1); wm1.has(o1); // false
Set 和 Map 數據結構