ES6從入門到精通——Symbol、Map 與 Set
Symbol
ES6 引入了一種新的原始資料型別Symbol,表示獨一無二的值。它是 JavaScript 語言的第七種資料型別,前六種是:undefined、null、布林值(Boolean)、字串(String)、數值(Number)、物件(Object)。
基本用法
Symbol 值通過Symbol
函式生成,Symbol
函式前不能使用new
命令,否則會報錯。這是因為生成的 Symbol 是一個原始型別的值,不是物件。
let s1 = Symbol();
let s2 = Symbol();
s1 // Symbol()
s2 // Symbol()
在生成 Symbol 值的時候,可以接受一個字串作為引數,表示對 Symbol 例項的描述,主要是為了在控制檯顯示,或者轉為字串時,比較容易區分。
let s1 = Symbol('foo');
let s2 = Symbol('bar');
s1 // Symbol(foo)
s2 // Symbol(bar)
description
建立 Symbol 的時候,可以新增一個描述。如果我們想要得到這個描述,我們可以通過 description
屬性直接得到 Symbol 的描述。
const sym = Symbol('foo');
sym.description // "foo"
屬性名 Symbol
由於每一個 Symbol 值都是不相等的,這意味著 Symbol 值可以作為識別符號,用於物件的屬性名,就能保證不會出現同名的屬性。這對於一個物件由多個模組構成的情況非常有用,能防止某一個鍵被不小心改寫或覆蓋。
let mySymbol = Symbol();
// 第一種寫法
let a = {};
a[mySymbol] = 'Hello!';
// 第二種寫法
let a = {
[mySymbol]: 'Hello!'
};
// 以上寫法都得到同樣結果
a[mySymbol] // "Hello!"
在使用過程中需要注意的是,當Symbol 值作為物件屬性名時,不能用點運算子,點運算子後面總是字串,所以不會讀取mySymbol作為標識名所指代的那個值。
let syObject = {};
syObject[sy] = "kk";
syObject[ sy]; // "kk"
syObject.sy; // undefined
Symbol 值作為屬性名時,該屬性是公有屬性不是私有屬性,可以在類的外部訪問。但是不會出現在 for…in 、 for…of 的迴圈中,也不會被 Object.keys() 、 Object.getOwnPropertyNames() 返回。
let syObject = {};
syObject[sy] = "kk";
console.log(syObject);
for (let i in syObject) {
console.log(i);
} // 無輸出
Object.keys(syObject); // []
如果要讀取到一個物件的 Symbol 屬性,可以通過 Object.getOwnPropertySymbols()
和 Reflect.ownKeys()
取到。Object.getOwnPropertySymbols()
方法可以獲取物件所有 Symbol 屬性名。Reflect.ownKeys()
方法可以返回所有型別的鍵名,包括常規鍵名和 Symbol 鍵名。
Object.getOwnPropertySymbols(syObject); // [Symbol(key1)]
Reflect.ownKeys(syObject); // [Symbol(key1)]
Symbol.for(),Symbol.keyFor()
Symbol.for()
與Symbol()
這兩種寫法,都會生成新的 Symbol。而它們的區別,前者可以重新使用已經生成的 Symbol 值。Symbol.for()
不會每次呼叫就返回一個新的 Symbol 型別的值,而是會先檢查給定的key
是否已經存在,如果不存在才會新建一個值。
Symbol.for("bar") === Symbol.for("bar")
// true
Symbol("bar") === Symbol("bar")
// false
Symbol.keyFor()
方法返回一個已登記的 Symbol 型別值的key
。
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined
Map物件
ES6 提供了 Map 資料結構。它類似於物件,也是鍵值對的集合,但是“鍵”的範圍不限於字串,各種型別的值(包括物件)都可以當作鍵。Map物件和object物件之間的區別:
- 一個 Object 的鍵只能是字串或者 Symbols,但一個 Map 的鍵可以是任意值。
- Map 中的鍵值是有序的(FIFO 原則),而新增到物件中的鍵則不是。
- Map 的鍵值對個數可以從 size 屬性獲取,而 Object 的鍵值對個數只能手動計算。
- Object 都有自己的原型,原型鏈上的鍵名有可能和你自己在物件上的設定的鍵名產生衝突。
在建立 Map物件的時候,可以使用無參構造建立,也可以傳入一個數組作為引數,會自動將陣列轉化為Map 物件格式的資料。
const m = new Map();
const map = new Map([
['name', '張三'],
['title', 'Author']
]);
不僅僅是陣列,任何具有 Iterator 介面、且每個成員都是一個雙元素的陣列的資料結構都可以當作Map建構函式的引數。Set
和Map
都可以用來生成新的 Map
屬性和操作方法
size
屬性返回 Map 結構的成員總數。set
方法設定鍵名key
對應的鍵值為value
,然後返回整個 Map 結構。如果key
已經有值,則鍵值會被更新,否則就新生成該鍵。set
方法返回的是當前的Map物件,因此可以採用鏈式寫法。get
方法讀取key
對應的鍵值,如果找不到key
,返回undefinedhas
方法返回一個布林值,表示某個鍵是否在當前 Map 物件之中。delete
方法刪除某個鍵,返回true。如果刪除失敗,返回false。clear
方法清除所有成員,沒有返回值。
const map = new Map();
map.set('foo', a);
map.set('bar', b).set('bar', c);
map.size // 2
map.get('bar') //c
map.has('bar'); //true
map.delete('bar');//true
map.has('bar'); //false
map.clear('bar');
map.has('bar'); //false
遍歷方法
- Map.prototype.keys():返回鍵名的遍歷器。
- Map.prototype.values():返回鍵值的遍歷器。
- Map.prototype.entries():返回所有成員的遍歷器。
- Map.prototype.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 與陣列互換互相轉換
const myMap = new Map()
.set(true, 7)
.set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
new Map([
[true, 7],
[{foo: 3}, ['abc']]
])
// Map {
// true => 7,
// Object {foo: 3} => ['abc']
// }
Set物件
Set物件類似於陣列,但是成員的值都是唯一的,沒有重複的值,可以儲存任何型別的唯一值,無論是原始值或者是物件引用。
在生成 Set 物件時,可以傳入一個數組(或者具有 iterable 介面的其他資料結構)作為引數,用來初始化
//無參構造
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
//有參構造
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
屬性和方法
size
屬性返回 Set 例項的成員總數。add(value)
新增某個值,返回 Set 結構本身。delete(value)
刪除某個值,返回一個布林值,表示刪除是否成功。has(value)
返回一個布林值,表示該值是否為Set的成員。clear()
清除所有成員,沒有返回值。
s.add(1).add(2).add(2);
// 注意2被加入了兩次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
遍歷方法
- keys():返回鍵名的遍歷器
- values():返回鍵值的遍歷器
- entries():返回鍵值對的遍歷器
- forEach():使用回撥函式遍歷每個成員
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"]
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
也可以使用 for…of 迴圈遍歷 Set
let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
console.log(x);
}
// red
// green
// blue