Symbol學習筆記
1.概述
ES5的物件屬性名都是字串,這容易造成屬性名的衝突。ES6引入了一種新的原始資料型別Symbol,表示獨一無二的值。它是JavaScript語言的第七種資料型別,前六種是:undefined、null、布林值、字串、數值、物件。
Symbol值通過Symbol函式生成。這就是說,物件的屬性名現在可以有兩種型別,一種是原來的字串,另一種就是新增的Symbol型別。凡是屬性名屬於Symbol型別,就都是獨一無二的,可以保證不會與其它屬性名產生衝突。
let s = Symbol(); typeof s//symbol
上面程式碼中,變數s就是一個獨一無二的值。typeof運算子的結果,表明變數s是Symbol資料型別,而不是字串之類的其它型別。
Symbol函式可以接受一個字串作為引數,表示對Symbol例項的描述,主要是為了控制檯顯示,或者轉為字串時,比較容易區分。
let s1 = Symbol('foo'); let s2 = Symbol('bar'); s1//Symbol(foo); s2//Symbol(bar); s1.toString()//'Symbol(foo)' s2.toString()//'Symbol(bar)'
如果Symbol的引數是一個物件,就會呼叫該物件的toString方法,將其轉為字串,然後才生成一個Symbol值。
const obj = { toString() { return 'abc'; } }; const sym = Symbol(obj); sym // Symbol(abc)
注意:Symbol函式的引數只是表示對當前Symbol值的描述,因此相同引數Symbol函式的返回值是不相等的。
// 沒有引數的情況 let s1 = Symbol(); let s2 = Symbol(); s1 === s2 // false // 有引數的情況 let s1 = Symbol('foo'); let s2 = Symbol('foo'); s1 === s2 // false
上面程式碼中,s1和s2都是symbol函式的返回值,而且引數相同,但是它們是不相等的。
2.作為屬性名的Symbol
由於每一個Symbol值都是不相同的,這意味著Symbol值可以作為識別符號,用於物件的屬性名,就能保證不會出現同名的屬性。這對於一個物件由多個模組構成的情況非常有用,能防止某一個鍵被不小心改寫或覆蓋。
let mySymbol = Symbol(); //第一種寫法 let a = {}; a[mySymbol] = 'Hello!'; //第二種寫法 let a = { [mySymbol]:'Hello!' }; //第三種寫法 let a = {}; Object.defineProperty(a,mySymbol,{value:'Hello!'}) //以上寫法都得到同樣的結果 a[mySymbol]//'Hello!'
上面程式碼通過方括號結構和Object.defineProperty,將物件的屬性名指定為一個Symbol值。注意:Symbol值作為物件屬性名時,不能用點運算子。
const mySymbol = Symbol(); const a = {}; a.mySymbol = 'Hello!'; a[mySymbol]//undefined; a['mySymbol']//'Hello!'
上面程式碼中,因為點運算子後面總是字串,所以不會讀取mySymbol作為標識名所指代的那個值,導致a的屬性名實際上是一個字串,而不是一個Symbol值。
同理,在物件的內部,使用Symbol值定義屬性時,Symbol值必須放在方括號之中。
let s = Symbol(); let obj = { [s]:function(arg){...} }
上面程式碼中,如果s不放在方括號中,該屬性的鍵名就是字串s,而不是s所代表的那個Symbol值。採用增強的物件寫法,上面程式碼的obj物件可以寫的更簡潔一些。
let obj = { [s](arg){...} }
Symbol型別還可以用於定義一組常量,保證這組常量的值都是不相等的。
const log = {}; log.levels = { DEBUG: Symbol('debug'), INFO: Symbol('info'), WARN: Symbol('warn') } console.log(log.levels.DEBUG, 'debug message'); console.log(log.levels.INFO, 'info message');
下面是另一個例子。
const COLOR_RED= Symbol(); const COLOR_GREEN= Symbol(); function getComplement(color) { switch (color) { case COLOR_RED: return COLOR_GREEN; case COLOR_GREEN: return COLOR_RED; default: throw new Error('Undefined color'); } }
常量使用 Symbol 值最大的好處,就是其他任何值都不可能有相同的值了,因此可以保證上面的switch語句會按設計的方式工作。