第11章 Set 和 Map 數據結構
參考資料:《ES6標準入門第3版》
目錄
11.1 Set 數據結構
11.1.1 基本用法
ES6 提供了新的數據結構 Set。它類似於數組,但是成員的值都是唯一的,沒有重複的值。相當於數學中的集合。
Set
本身是一個構造函數,用來生成 Set 數據結構。
1. Set 結構不會添加重複的值。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
跟數組不一樣,Set 實例通過 add() 方法添加成員,在添加成員的時候,若Set 結構中已經存在該值,則該成員不會被添加。
2. Set 函數可以接受一個數組作爲參數來初始化
const set = new Set([1, 2, 3, 4, 4, 4, 4, 4]);
console.log([...set])
// [1, 2, 3, 4]
console.log(set.size)
// 4
訪問 Set 數據結構的變量的大小隻能通過 set.size 來訪問,set.length 結果是 undefined
3. Set 數據結構中的類型判斷
由於 Set 數據結構的特性,可以很方便地實現去除數組重複成員。
var array = [1, 2, 3, 3, 4, 4, 5, 5]
var newarray = [...new Set(array)]
console.log(newarray)
// [ 1, 2, 3, 4, 5 ]
這裏有必要討論下 Set 如何判斷兩個值是否相等。
Set 對加入的值不會發生類型轉換,5 和 “5” 是兩個不同的值。
const set = new Set()
set.add(5)
set.add('5')
console.log(set.size) // 2
Set 內部判斷兩個值的是否相同的方法類似於精確相等運算符( === ),不同的是在 Set 結構中 NaN 等於本身,而精確相等運算符認爲 NaN 不等於本身。
console.log(NaN === NaN)
// false
const set = new Set()
set.add(NaN)
set.add(NaN)
console.log(set.size)
// size = 1, 說明在 set 內部認爲 NaN 相等,只添加了一個值
另外,無論對象是否爲空,兩個對象總是不相等。
const set = new Set()
set.add({})
set.add({})
console.log(set.size) // 2
11.1.2 Set 實例的屬性和方法
Set 結構的實例有以下屬性:
Set.prototype.constructor
:構造函數,默認就是Set
函數。Set.prototype.size
:返回Set
實例的成員總數。
Set 結構有如下方法:
add(value)
:添加某個值,返回 Set 結構本身。delete(value)
:刪除某個值,返回一個布爾值,表示刪除是否成功。has(value)
:返回一個布爾值,表示該值是否爲Set
的成員。clear()
:清除所有成員,沒有返回值
11.1.3 遍歷操作
Set 結構的實例有四個遍歷方法,可以用於遍歷成員。
keys()
:返回鍵名的遍歷器values()
:返回鍵值的遍歷器entries()
:返回鍵值對的遍歷器forEach()
:使用回調函數遍歷每個成員
由於 Set 結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值),所以keys
方法和values
方法的行爲完全一致。
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
Set 結構的實例默認可遍歷,可以省略values
方法,直接用for...of
循環遍歷 Set。
let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
console.log(x);
}
// red
// green
// blue
11.2 Map 結構
11.2.1 基本用法
ES6 提供了 Map 數據結構,它類似於對象,也是鍵值對的集合,但是“鍵”的範圍不限於字符串,各種類型的值(包括對象)都可以當作鍵。
const map = new Map();
const object = {
word: 'Hello World'
};
map.set(object, 'content')
console.log(map.get(object)) // "content"
console.log(map.has(object)) // true
map.delete(object) // true
console.log(map.has(object)) // false
上面的例子就是把一個對象當做 map 的一個鍵,把一個字符串 “content” 作爲該對象鍵對應的值,map 可以正常讀取、或刪除這個鍵值對。
Map 結構除了可以使用 set 方法來添加成員,也可以接受一個數組作爲參數,該數組的成員是一個個表示鍵值對的數組。
const items = [
['name1', '張三'],
['name2', '李四']
];
const map = new Map(items);
console.log(map.size) // 2
console.log(map.has('name1')) // true
console.log(map.get('name1')) // 張三
如果對同一個鍵多次賦值,後面的值將會覆蓋前面的值。
const map = new Map();
map
.set(1, 'aaa')
.set(1, 'bbb');
map.get(1) // "bbb"
讀取一個未知的值返回 undefined。
new Map().get('abc')
// undefined
注意點:
1. 只有對同一個對象的引用,Map 結構纔將其視爲同一個鍵。
const map = new Map();
let b = ['b']
map.set(['a'], 555)
map.get(['a']) // undefined
map.set(b, 666)
map.get(b) // 666
2. 如果 Map 的鍵是一個簡單類型的值(數字、字符串、布爾值),則只要兩個值嚴格相等,Map 將其視爲一個鍵。
let map = new Map();
map.set(-0, 123);
map.get(+0) // 123
map.set(true, 1);
map.set('true', 2);
map.get(true) // 1
map.set(undefined, 3);
map.get(undefined) // 3
map.set(null, 4);
map.get(null) // 4
map.set(NaN, 123);
map.get(NaN) // 123
在上述代碼的描述中,+0 與 -0 相等,同時 Map 將 NaN 視爲同一個鍵,null 和 undefined 也被視爲不同的鍵。
11.2.2 Map 實例的屬性和方法
(1)size 屬性 :size 屬性返回 Map 結構的成員總數。
(2)set(key, value) :set
方法設置鍵名key
對應的鍵值爲value
,然後返回整個 Map 結構。如果key
已經有值,則鍵值會被更新,否則就新生成該鍵。
set
方法返回的是當前的Map
對象,因此可以採用鏈式寫法。
let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');
(3)get(key):get
方法讀取key
對應的鍵值,如果找不到key
,返回undefined
。
(4)has(key):has
方法返回一個布爾值,表示某個鍵是否在當前 Map 對象之中。
(5)delete(key):delete
方法刪除某個鍵,返回true
。如果刪除失敗,返回false
。
(6)clear():clear
方法清除所有成員,沒有返回值。
11.2.3 Map 遍歷方法
Map 結構原生提供三個遍歷器生成函數和一個遍歷方法。
keys()
:返回鍵名的遍歷器。values()
:返回鍵值的遍歷器。entries()
:返回所有成員的遍歷器。forEach()
:遍歷 Map 的所有成員。
需要特別注意的是,Map 的遍歷順序就是插入順序。
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
// 等同於使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
11.2.4 Map 與其他數據類型的轉換
1. Map 與數組的互相轉換
Map 結構轉換爲數組結構的比較快速的方法是結合擴展運算符 (...)。
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
console.log([...map])
// [ [ 1, 'one' ], [ 2, 'two' ], [ 3, 'three' ] ]
數據結構轉爲Map直接將數組作爲參數傳入Map構造函數就可以了,就如上述代碼中的數組所示。
2. Map 與對象的互相轉換
如果所有 Map 的鍵都是字符串,它可以無損地轉爲對象。
function strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k,v] of strMap) {
obj[k] = v;
}
return obj;
}
const myMap = new Map()
.set('yes', true)
.set('no', false);
strMapToObj(myMap)
// { yes: true, no: false }
如果有非字符串的鍵名,那麼這個鍵名會被轉成字符串,再作爲對象的鍵名。
對象轉爲 Map,本質上就是遍歷對象的所有屬性,然後用 set 方法爲 Map 結構添加成員。
function objToStrMap(obj) {
let strMap = new Map();
for (let k of Object.keys(obj)) {
strMap.set(k, obj[k]);
}
return strMap;
}
objToStrMap({yes: true, no: false})
// Map {"yes" => true, "no" => false}