概述
ES6 引入了一種新的原始數(shù)據(jù)類型 Symbol,表示獨一無二的值。Symbol 值通過 Symbol 函數(shù)生成;
ES6 之后的數(shù)據(jù)類型
| undefined | null | Boolean | String | Number | Object | Symbol | |
|---|---|---|---|---|---|---|---|
| 是否引用類型 | 否 | 否 | 否 | 否 | 否 | 是 | 否 |
Symbol()
| 使用方法 | 返回值 |
|---|---|
| Symbol(str) | Symbol 類型的數(shù)據(jù) |
let s1 = Symbol("foo");
let s2 = Symbol("bar");
s1; // Symbol(foo)
s2; // Symbol(bar)
s1.toString(); // "Symbol(foo)"
s2.toString(); // "Symbol(bar)"
自述
Symbol(str) 類似一個加密和隨機數(shù)生成的結(jié)合體,下面的例子簡單模擬它的工作原理
const Symbol = (str = "") => {
return str + Math.random();
};
我們現(xiàn)在在用 redux 的時候經(jīng)常需要寫一堆的 action.type,如果我們用 Symbol 來生成的話可以省去很大的工作量,如下
// 最初始
dispatch({type:'action1',value:2})
dispatch({type:'action2',value:2})
// 傳統(tǒng)的
// config.js
export const actions = {
action1 : 'action1'
action2 : 'action2'
... // 幾十上百次重復(fù)勞動
}
// actions.js
import { actions } from './config.js'
const {action1, action2} = actions
dispatch({type:action1,value:2})
dispatch({type:action2,value:2})
// 新的
// config.js
const actions = {}
['action1','action2',...].forEach(key =>{ actions[key] = Symbol() });
export actions
// actions.js
import { actions } from './config.js'
const {action1, action2} = actions
dispatch({type:action1,value:2})
dispatch({type:action2,value:2})
通過對比上面三種使用方法我們會發(fā)現(xiàn),第一種最不容易維護,一旦我要改變某個 action.type 我需要去翻所有的頁面,第二種和第三者維護成本相當,但是因為我并不需要確切的知道 action.type 是什么,我只要知道我發(fā)出的 action 和我的處理函數(shù)能夠?qū)?yīng)就好了,所以很顯然,完成同樣功能的清空像第三種方式可以讓我節(jié)省接近一半的代碼量。
Symbol 帶來的問題
Symbol 作為屬性名,該屬性不會出現(xiàn)在 for...in、for...of 循環(huán)中,也不會被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
這樣會導(dǎo)致我們常用的 Object.keys() 出現(xiàn)問題,獲取不到全部的 key
// 這里拿不到Symbol類型 的key
Object.keys(obj).forEach(key => {})
那怎么辦呢?說了這么多,難道我們只能 Symbol 和 Object.keys()二選一? 當然不會了
另一個新的 API,Reflect.ownKeys 方法可以返回所有類型的鍵名,包括常規(guī)鍵名和 Symbol 鍵名。
let obj = {
[Symbol("my_key")]: 1,
enum: 2,
nonEnum: 3
};
Reflect.ownKeys(obj);
// ["enum", "nonEnum", Symbol(my_key)]
Symbol.for(),Symbol.keyFor()
有時,我們希望重新使用同一個 Symbol 值,Symbol.for 方法可以做到這一點。它接受一個字符串作為參數(shù),然后搜索有沒有以該參數(shù)作為名稱的 Symbol 值。如果有,就返回這個 Symbol 值,否則就新建并返回一個以該字符串為名稱的 Symbol 值。
let s1 = Symbol.for("foo");
let s2 = Symbol.for("foo");
s1 === s2; // true
我們可以用一段代碼來簡單模擬這個 api 做的事
windows.Memory = {},
const Symbol_ = {
for(str) {
if (!Memory[str]) {
let s = Symbol(str);
Memory[str] = s;
return s;
}
return Memory[str];
}
};
let a = Symbol_.for("go");
let b = Symbol_.for("go");
a === b; // true
ES6 內(nèi)置的11個 Symbol 值
| name | 說明 |
|---|---|
| 對象的Symbol.hasInstance屬性 | 當其他對象使用instanceof運算符,判斷是否為該對象的實例時,會調(diào)用這個方法。比如,foo instanceof Foo在語言內(nèi)部,實際調(diào)用的是Foo[Symbol.hasInstance](foo)。 |