JavaScript ES6 Symbol 資料型態
ES6 引入了一種新的基本資料型態 (primitive data types),叫做 Symbol (符號),用來表示獨一無二 (unique) 的值。
什麼叫獨一無二?在 ES5 中,object 的屬性 (property) 只能是字串,如果你要幫一個 object 添加新的屬性,很容易會造成名稱衝突,Symbol 就是用來解決這件事情的。也就是說,object property name 現在可以有兩種型態 - 字串和 Symbol。
Symbol 值透過 Symbol()
函數來生成:
let s1 = Symbol();
// "symbol"
typeof s1;
let s2 = Symbol();
// false
s1 === s2;
Symbol() 是一個函數,不是 constructor,你不能 new 它:
// TypeError: Symbol is not a constructor
var s = new Symbol();
Symbol 函數也可以接受一個字串參數,來對該 Symbol 物件命名,主要是為了在 console 顯示,或轉型成字串時,方便區分不同的 Symbol:
let s1 = Symbol('foo');
let s2 = Symbol('bar');
// Symbol(foo)
s1;
// Symbol(bar)
s2;
// "Symbol(foo)"
s1.toString();
// "Symbol(bar)"
s2.toString();
// 建立一個和 s1 同名的 s3
let s3 = Symbol('foo');
// 雖然同名,但 s1 和 s3 還是不一樣的值
// false
s1 === s3;
Symbol 當作屬性名稱 (Properties)
由於每個 Symbol 值都是不相等的,所以用 Symbol 來當作物件的屬性名稱,可以確保不會出現同名的屬性,這特性能防止一個物件的屬性不會在其他地方被意外的覆蓋掉。
var mySymbol = Symbol();
// 下面三種寫法都可以定義一個 Symbol 屬性名稱
// 寫法 1
var a = {};
a[mySymbol] = 'Hello!';
// 寫法 2
var a = {
[mySymbol]: 'Hello!'
};
// 寫法 3
var a = {};
Object.defineProperty(a, mySymbol, {value: 'Hello!'});
// "Hello!"
a[mySymbol];
用 Symbol 當屬性名時,不能用 .
點運算子,因為用點運算子,會被當作是字串,而不是 Symbol:
var mySymbol = Symbol();
var o = {};
o.mySymbol = 'fooish.com';
// undefined
o[mySymbol];
// "fooish.com"
o['mySymbol'];
同樣道理,在 object literal 中加 Symbol 屬性,你必須用 computed property 語法:
let s = Symbol();
let obj = {
[s]: function() {}
};
Symbol 也常被用來宣告常數值 (constant),因為用 Symbol 你可以確保所有的值都是不同的:
const FLAG_A = Symbol();
const FLAG_B = Symbol();
function doSomething(flag) {
switch (flag) {
case FLAG_A:
// ...
break;
case FLAG_B:
// ...
break;
default:
throw new Error('Undefined flag');
}
}
Global Symbols - Symbol.for() / Symbol.keyFor()
有時候,你會希望重複利用同一個 Symbol 值,你可以用 Symbol.for()
和 Symbol.keyFor()
來存取全域的 Symbol 值 (global symbol registry)。
Symbol.for(key)
用來取得名稱為 key (字串) 的 global Symbol,如果不存在則會先建立一個新的存到 global symbol registry 後再返回Symbol.keyFor(sym)
用來取得某個 global Symbol 的 key 名稱
Symbol.for(key) 例子:
// 建立一個 global Symbol
Symbol.for('foo');
// 不會再重複建立,會直接返回已經建立的 Symbol
Symbol.for('foo');
// true
Symbol.for('bar') === Symbol.for('bar');
// false
Symbol('bar') === Symbol('bar');
// key 名稱也會被當成 Symbol 名稱
var sym = Symbol.for('mario');
// "Symbol(mario)"
sym.toString();
Symbol.keyFor(sym) 例子:
var globalSym = Symbol.for('foo');
// "foo"
Symbol.keyFor(globalSym);
var localSym = Symbol();
// 如果沒有這個 global Symbol 會返回 undefined
// undefined
Symbol.keyFor(localSym);
Object.getOwnPropertySymbols()
Symbol 的屬性名稱不能被遍歷,像是 for...in
, for...of
, Object.keys()
, Object.getOwnPropertyNames()
, JSON.stringify()
都不會返回 Symbol 屬性名。
你要取得所有的 Symbol 屬性名稱可以使用 Object.getOwnPropertySymbols()
方法,該方法會返回一個陣列。
var obj = {};
obj[Symbol('a')] = 'a';
obj[Symbol.for('b')] = 'b';
obj['c'] = 'c';
obj.d = 'd';
for (var i in obj) {
console.log(i);
}
// 依序輸出
// "c"
// "d"
var objectSymbols = Object.getOwnPropertySymbols(obj);
// [Symbol(a), Symbol(b)]
objectSymbols;