ES6 提供了新的數據結構 Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。html
Set
與數組之間的最大區別是:es6
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 後,你可能想要添加或刪除條目。如何操做呢?可使用名稱對應的 .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
使用 .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
和普通 Set
很像,可是具備如下關鍵區別:
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
結構有如下三個方法。
若是說 Set 相似於數組,那麼 Map 就相似於對象,由於 Map 存儲鍵值對,和對象包含命名屬性及值相相似。
本質上,Map
是一個能夠存儲鍵值對的對象,它相似於對象,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵。
要建立 Map,只需輸入
const employees = new Map();
console.log(employees); //Map {}
複製代碼
這樣就會建立空的 Map employee
,沒有鍵值對。
和 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構造函數的參數。這就是說,Set
和Map
均可以用來生成新的 Map
。
注意,只有對同一個對象的引用,Map
結構纔將其視爲同一個鍵。這一點要很是當心。
const map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
複製代碼
上面代碼的set
和get
方法,表面是針對同一個鍵,但實際上這是兩個值,內存地址是不同的,所以get
方法沒法讀取該鍵,返回undefined
。
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
和普通 Map
很像,可是具備如下關鍵區別:
你能夠像建立普通 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(…)
複製代碼
WeakMap
與 Map
在 API
上的區別主要是兩個,一是沒有遍歷操做(即沒有key()、values()
和entries()
方法),也沒有size
屬性。由於沒有辦法列出全部鍵名,某個鍵名是否存在徹底不可預測,跟垃圾回收機制是否運行相關。這一刻能夠取到鍵名,下一刻垃圾回收機制忽然運行了,這個鍵名就沒了,爲了防止出現不肯定性,就統一規定不能取到鍵名。二是沒法清空,即不支持clear方法。所以,WeakMap
只有四個方法可用:get()、set()、has()、delete()
。