1. 程式人生 > 其它 >ES6從入門到精通——Symbol、Map 與 Set

ES6從入門到精通——Symbol、Map 與 Set

技術標籤:ES6es6

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建構函式的引數。SetMap都可以用來生成新的 Map

屬性和操作方法

  • size屬性返回 Map 結構的成員總數。
  • set方法設定鍵名key對應的鍵值為value,然後返回整個 Map 結構。如果key已經有值,則鍵值會被更新,否則就新生成該鍵。set方法返回的是當前的Map物件,因此可以採用鏈式寫法。
  • get方法讀取key對應的鍵值,如果找不到key,返回undefined
  • has方法返回一個布林值,表示某個鍵是否在當前 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