ES6系列--10. set和map

9.set和map

set

ES6 提供了新的數據結構 Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。html

Set 與數組之間的最大區別是:es6

  • Set 不基於索引,不能根據集合中的條目在集合中的位置引用這些條目
  • Set 中的條目不能單獨被訪問

建立set

Set 自己是一個構造函數,用來生成 Set 數據結構。web

const games = new Set();
console.log(games);  //Set{}
複製代碼

此代碼會建立空的 Set games,其中沒有條目。數組

Set 函數也能夠接受一個數組(或者具備 iterable接口的其餘數據結構)做爲參數,用來初始化。bash

const set = new Set([1,2,3,3,4])
[...set]  //[1, 2, 3, 4]
set.size  //4

for (let i of s) {
  console.log(i);
}  //1, 2, 3, 4

const set = new Set('hello')
[...set] // ["h", "e", "l", "o"]

複製代碼

修改 Set

建立 Set 後,你可能想要添加或刪除條目。如何操做呢?可使用名稱對應的 .add() 和 .delete() 方法:數據結構

const set = new Set([1,2,3,3,4])
set.add(5)
set.add('hello')  
set.delete(1)
// Set{1, 2, 3, 4, 5,'hello'}
複製代碼

另外一方面,若是你想要刪除 Set 中的全部條目,可使用 .clear() 方法。app

set()
console.log(set);  //Set{}
複製代碼

提示:ide

  • 若是你嘗試向 Set.add() 重複的條目,系統不會報錯,可是該條目不會添加到 Set 中。此外,若是你嘗試 .delete() Set 中不存在的條目,也不會報錯,Set 保持不變。函數

  • .add() 添加無論成功與否,都會返回該 Set 對象。另外一方面,.delete() 則會返回一個布爾值,該值取決因而否成功刪除(即若是該元素存在,返回true,不然返回false)。ui

使用 Set

查看長度

使用 .size 屬性能夠返回 Set 中的條目數:

const months = new Set(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']);
console.log(months.size);  //12
複製代碼

注意,不能像數組那樣經過索引訪問 Set,所以要使用 .size 屬性,而不是 .length 屬性來獲取 Set 的大小。

檢查是否存在某個條目

使用 .has() 方法能夠檢查 Set 中是否存在某個條目。若是 Set 中有該條目,則 .has() 將返回 true。若是 Set 中不存在該條目,則 .has() 將返回 false

months.has('September')  //true
複製代碼

檢索全部值

Set 結構的實例有四個遍歷方法,能夠用於遍歷成員。

  • keys():返回鍵名的遍歷器
  • values():返回鍵值的遍歷器
  • entries():返回鍵值對的遍歷器
  • forEach():使用回調函數遍歷每一個成員

keys方法、values方法、entries方法返回的都是遍歷器對象,因爲 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"]
複製代碼

上面代碼中,entries方法返回的遍歷器,同時包括鍵名和鍵值,因此每次輸出一個數組,它的兩個成員徹底相等。

Set 結構的實例與數組同樣,也擁有forEach方法,用於對每一個成員執行某種操做,沒有返回值。

set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
複製代碼

WeakSet

WeakSet 和普通 Set 很像,可是具備如下關鍵區別:

  • WeakSet 只能包含對象
  • WeakSet 沒法迭代,意味着不能循環訪問其中的對象
  • WeakSet 沒有 .clear() 方法
const student1 = { name: 'James', age: 26, gender: 'male' };
const student2 = { name: 'Julia', age: 27, gender: 'female' };
const student3 = { name: 'Richard', age: 31, gender: 'male' };

const roster = new WeakSet([student1, student2, student3]);
console.log(roster);
//WeakSet {Object {name: 'Julia', age: 27, gender: 'female'}, Object {name: 'Richard', age: 31, gender: 'male'}, Object {name: 'James', age: 26, gender: 'male'}}
複製代碼

可是若是你嘗試添加對象之外的內容,系統將報錯!

roster.add('Amanda'); //Uncaught TypeError: Invalid value used in weak set(…)
複製代碼

其次,WeakSet 中的對象都是弱引用,即垃圾回收機制不考慮 WeakSet 對該對象的引用,也就是說,若是其餘對象都再也不引用該對象,那麼垃圾回收機制會自動回收該對象所佔用的內存,不考慮該對象還存在於 WeakSet 之中。

這是由於垃圾回收機制依賴引用計數,若是一個值的引用次數不爲0,垃圾回收機制就不會釋放這塊內存。結束使用該值以後,有時會忘記取消引用,致使內存沒法釋放,進而可能會引起內存泄漏。WeakSet 裏面的引用,都不計入垃圾回收機制,因此就不存在這個問題。所以,WeakSet 適合臨時存放一組對象,以及存放跟對象綁定的信息。只要這些對象在外部消失,它在 WeakSet 裏面的引用就會自動消失。

因爲上面這個特色,WeakSet 的成員是不適合引用的,由於它會隨時消失。另外,因爲 WeakSet 內部有多少個成員,取決於垃圾回收機制有沒有運行,運行先後極可能成員個數是不同的,而垃圾回收機制什麼時候運行是不可預測的,所以 ES6 規定 WeakSet 不可遍歷。

這些特色一樣適用於後面要介紹的 WeakMap 結構。

WeakSet 結構有如下三個方法。

  • WeakSet.prototype.add(value):向 WeakSet 實例添加一個新成員。
  • WeakSet.prototype.delete(value):清除 WeakSet 實例的指定成員。
  • WeakSet.prototype.has(value):返回一個布爾值,表示某個值是否在 WeakSet 實例之中。

Map

若是說 Set 相似於數組,那麼 Map 就相似於對象,由於 Map 存儲鍵值對,和對象包含命名屬性及值相相似。

本質上,Map是一個能夠存儲鍵值對的對象,它相似於對象,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵。

建立 Map

要建立 Map,只需輸入

const employees = new Map();
console.log(employees); //Map {}
複製代碼

這樣就會建立空的 Map employee,沒有鍵值對。

修改 Map

和 Set 不一樣,你沒法使用值列表建立 Map;而是使用 Map 的 .set() 方法添加鍵值。

.set() 方法有兩個參數。第一個參數是鍵,用來引用第二個參數,即值。

要移除鍵值對,只需使用 .delete() 方法。

const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false
m.clear() //Map {}
複製代碼

一樣,和 Set 相似,你可使用 .clear() 方法從 Map 中刪除全部鍵值對。

提示:若是你使用 .set()Map 中添加鍵已存在的鍵值對,不會收到錯誤,可是該鍵值對將覆蓋 Map 中的現有鍵值對。此外,若是嘗試使用 .delete() 刪除 Map 中不存在的鍵值,不會收到錯誤,而 Map 會保持不變。

若是成功地刪除了鍵值對,.delete() 方法會返回 true,失敗則返回 false.set() 若是成功執行,則返回 Map 對象自己。

Map 也能夠接受一個數組做爲參數。該數組的成員是一個個表示鍵值對的數組。

const map = new Map([
  ['name', '張三'],
  ['title', 'Author']
]);

map.size // 2
map.has('name') // true
map.get('name') // "張三"
map.has('title') // true
map.get('title') // "Author"

const set = new Set([
  ['foo', 1],
  ['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1

const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3
複製代碼

事實上,不只僅是數組,任何具備 Iterator 接口、且每一個成員都是一個雙元素的數組的數據結構均可以看成Map構造函數的參數。這就是說,SetMap均可以用來生成新的 Map

注意,只有對同一個對象的引用,Map 結構纔將其視爲同一個鍵。這一點要很是當心。

const map = new Map();

map.set(['a'], 555);
map.get(['a']) // undefined
複製代碼

上面代碼的setget方法,表面是針對同一個鍵,但實際上這是兩個值,內存地址是不同的,所以get方法沒法讀取該鍵,返回undefined

遍歷方法

Map 結構原生提供三個遍歷器生成函數和一個遍歷方法。

  • keys():返回鍵名的遍歷器。
  • values():返回鍵值的遍歷器。
  • entries():返回全部成員的遍歷器。
  • forEach():遍歷 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"
複製代碼

上面代碼最後的那個例子,表示Map 結構的默認遍歷器接口(Symbol.iterator屬性),就是entries方法。

map[Symbol.iterator] === map.entries
// true
複製代碼

Map 結構轉爲數組結構,比較快速的方法是使用擴展運算符(...)。

const map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

[...map.keys()]
// [1, 2, 3]

[...map.values()]
// ['one', 'two', 'three']

[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]

[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
複製代碼

WeakMap

WeakMap 和普通 Map 很像,可是具備如下關鍵區別:

  • WeakMap 只能包含對象做爲鍵,
  • WeakMap 沒法迭代,意味着沒法循環訪問,而且
  • WeakMap 沒有 .clear() 方法。
  • WeakMap的鍵名所指向的對象,不計入垃圾回收機制。

你能夠像建立普通 Map 那樣建立 WeakMap,可是須要使用 WeakMap 構造函數。

const book1 = { title: 'Pride and Prejudice', author: 'Jane Austen' };
const book2 = { title: 'The Catcher in the Rye', author: 'J.D. Salinger' };
const book3 = { title: 'Gulliver's Travels', author: 'Jonathan Swift' }; const library = new WeakMap(); library.set(book1, true); library.set(book2, false); library.set(book3, true); 複製代碼

可是若是你嘗試添加對象之外的內容做爲鍵,系統將報錯!

library.set('The Grapes of Wrath', false); //Uncaught TypeError: Invalid value used as weak map key(…)
複製代碼

WeakMapMapAPI 上的區別主要是兩個,一是沒有遍歷操做(即沒有key()、values()entries()方法),也沒有size屬性。由於沒有辦法列出全部鍵名,某個鍵名是否存在徹底不可預測,跟垃圾回收機制是否運行相關。這一刻能夠取到鍵名,下一刻垃圾回收機制忽然運行了,這個鍵名就沒了,爲了防止出現不肯定性,就統一規定不能取到鍵名。二是沒法清空,即不支持clear方法。所以,WeakMap只有四個方法可用:get()、set()、has()、delete()


ES5對象與ES6 Maps的異同

相關文章
相關標籤/搜索