Symbol是ES6中新引入的一種基本數據類型,在此以前Javascript中已有幾種基本數據類型:函數
不一樣於其餘基本類型的通俗易懂,Symbol
是什麼和有什麼用一直有些讓人困惑。this
JavaScript標準中規定對象的key只能是 String
或 Symbol
類型,區別在於 String
類型的key能夠重複而 Symbol
類型的key是惟一的。Symbol
的本質是表示一個惟一標識。每次建立一個Symbol,它所表明的值都不可能重複,該值的內部實現能夠視爲一段數字(相似:3423498431987719455..
)。因此理論上 Symbol 的存在只有一個意義:用於必須使用惟一值的場景。spa
建立 Number、String等基本類型的實例有兩種方法:經過構造函數(或者叫工廠函數)和文字語法糖。好比:debug
// 構造函數 const num = Number(3); const str = String('hi'); // 語法糖 const num = 3; const str = 'hi';
顯然使用語法糖更加簡潔。可是 Symbol 只能經過構造函數 Symbol()
進行建立:code
const sym = Symbol();
或者,咱們能夠傳入一個字符串參數(descriptor)用於描述該Symbol:對象
const sym = Symbol('cat');
注意:傳入的參數對 Symbol 值的產生並沒有影響,由於就算每次傳入的參數都同樣,生成的Symbol值也是不等的。該參數的做用僅用於描述被建立的Symbol,以便debug時能夠識別出Symbol的含義。 因此,下列等式結果爲 false
:blog
Symbol('cat') === Symbol('cat') // false
Symbol.for(key)
和 Symbol()
相似,Symbol.for(key)
也能夠建立一個Symbol,不同的是:建立的 Symbol 是全局的(在全局Symbol表中註冊),而若是全局已經存在相同 key
的Symbol,則直接返回該Symbol。因此,下列等式結果爲 true
:ip
Symbol.for('cat') === Symbol.for('cat') // true
其實 Symbol 自己很簡單,可是如何把它用好、且用的恰到好處卻令人困惑,由於在日常工做中並無多少非Symbol不用的場景。可是用對了Symbol會對你的代碼質量有很多提高。來看下面幾種案例:rem
使用Symbol做爲Object的key,能夠保證和其餘key都不重複。所以,Symbol很是適合用於對對象的屬性進行拓展。字符串
好比,當使用 String 做爲對象的key時,一旦出現重複的key則後面的屬性會覆蓋前面的:
const persons = { 'bruce': 'wayne', 'bruce': 'banner' } console.log(persons.bruce); // 'wayne'
使用Symbol做爲Key能夠避免這種狀況:
const bruce1 = Symbol('bruce'); const bruce2 = Symbol('bruce'); const persons = { [bruce1]: 'wayne', [bruce2]: 'banner' } console.log(persons[bruce1]); // 'wayne' console.log(persons[bruce2]); // 'banner'
JS不少內建的方法都是經過 Symbol 進行指定的,好比:Symobol.iterator
指定了一個iterable對象的迭代器方法;Symbol.replace
指定了對象字符串替換的方法,這類 Symbol 被稱爲 Well-know Symbols,表明了JS語言的內部行爲。
因爲Javascript並不自帶枚舉類型,一般狀況下咱們會使用一個freezed的Object來模擬枚舉類型,好比定義一個日期的枚舉:
const DAYS = Object.freeze({ monday: 1, tuesday: 2, wednesday: 3 });
此時有一個方法,接收 DAYS
的枚舉值來返回當天要作的事:
function getTodo(day) { switch (day) { case DAYS.monday: return "看電影"; case DAYS.tuesday: return "購物"; case DAYS.wednesday: return "健身"; default: return "日期錯誤"; } }
咱們但願代碼邏輯足夠嚴謹,傳入的參數嚴格按照 DAYS.monday
的形式,不然就返回日期錯誤,可是該枚舉類型的實現卻作不到。好比:getTodo(1)
依然能獲得 「看電影」 這個結果。
可是使用Symbol卻能夠解決這一問題,DAYS
枚舉類型能夠從新定義爲:
const DAYS = Object.freeze({ monday: Symbol('monday'), tuesday: Symbol('tuesday'), wednesday: Symbol('wednesday') });
此時 getTodo
方法必須接收 DAYS.monday
這樣的枚舉值做爲參數,不然就返回 「日期錯誤」,由於世界上再沒有任何一個值和 DAYS.monday
相等了。
這樣定義枚舉顯然更嚴謹了。
Key爲Symbol類型的屬性是不能被枚舉的,這是 Symbol 除了惟一性外的第二大特性,所以使用for...in
,Object.keys()
、Object.hasOwnProperty()
等方法不能識別Symbol屬性,簡而言之Symbol屬性對用戶是「隱藏」的(但並非private的,由於有其餘途徑能夠獲取Symbol屬性),例如:
所以Symbol做爲「隱藏」屬性能夠用來存儲對象的元數據。好比,有一個 TodoList
:
class TodoList { constructor() { // todo數量 this.count = 0; } // 增長todo add(id, content) { this[id] = content; this.count++; } } const list = new TodoList();
咱們使用 add()
方法向其中增長几個todo:
list.add('a', '看電影'); list.add('b', '購物'); list.add('c', '健身');
當咱們想使用 for...in
查看裏面全部的todo時,會把 count
屬性也帶出來:
爲了隱藏count屬性,更方便的對todo進行操做,咱們可使用Symbol來存儲它,TodoList
類修改成:
const count = Symbol('count'); class TodoList { constructor() { this[count] = 0; } add(id, content) { this[id] = content; this[count]++; } }
當咱們再遍歷 TodoList
的時候,count就隱藏了:
當咱們想獲取存儲在Symbol中的原數據時,可使用 Object.getOwnPropertySymbols()
方法:
以上是我能想到的 Symbol 的用途,若是你們有其餘心得體會歡迎補充。