符號


在 ECMAScript 6 中,基本資料型態除了 numberstring 與布林值 boolean,還多了一個 symbol

在過去,若需要表示一個獨立無二的符號(名稱),會使用字串來臨時權充,然而,字串本身可用來描述人、事、時、地、物等,雖然也可用來描述符號,不過,同樣 'x',在 A 場合可能代表某個意義,然而 B 場合可能就代表另一意義。

若確實需要的是一個獨一無二的符號,在 ECMAScript 6 中,可以使用 symbol,它是專門為符號而設計,可以使用 Symbol 函式來建立 symbol 型態的值。例如:

> let x = Symbol()
undefined
> let y = Symbol('The description of this symbol')
undefined
>

使用 Symbol 時指定的字串只是個描述,呼叫 Symbol 時可以不指定描述,若指定了,該描述只是對符號的說明,不代表符號!

在 A 場合定義了 Symbol('x'),那這個符號的定義就是獨一無二的,如果 B 場合定義了 Symbol('x'),它是另一個獨一無二的定義。具體來說,Symbol('x') === Symbol('x') 會是 false

> Symbol('x') === Symbol('x')
false
> 

每個新建立的符號都會是獨一無二,只不過它們都剛好有個描述叫做 'x',建立符號時指定的字串,應該用來描述符號的用途,對符號使用 toString,傳回的字串會是把描述放在 Symbol() 之中:

> Symbol('for each method of xxx').toString()
'Symbol(for each method of xxx)'
>

另一個建立符號的方式是 Symbol.for,你傳給它一個字串描述,它會在全域的符號註冊表(Symbol Registry)中看看,是不是有相同字串描述的符號,如果有就傳回,如果沒有就使用指定的字串作為描述建立一個新符號。

> let s1 = Symbol.for('symbol 1 for testing');
undefined
> let s = Symbol.for('symbol 1 for testing');
undefined
> s1 === s;
true
> let s2 = Symbol.for('symbol 2 for testing');
undefined
> s1 === s2;
false
>

本來是為了避免使用字串描述來代表符號,現在又使用字串描述來查找符號註冊表,這是件很怪的事,目前看來也沒別的替代方案,從另一方面來說,也應該避免使用全域符號註冊表,因為它是…呃…全域嘛!若真的需要使用 Symbol.for 來建立新符號放在全域符號註冊表,得有非常好的理由,證明它真的可以是全域適用,而且儘量指定一個獨一無二的字串,像是指定網域、組織名稱作為前置文字之類的。

除了全域符號註冊表之外,ES6 也有一些 Symbol 上的內建特性儲存著符號,像是 Symbol.iterator 代表著迭代器符號:

> Symbol.iterator
Symbol(Symbol.iterator)
>

ES6 中如果物件有函式(方法)可傳回迭代器,該函式會以 Symbol.iterator 符號作為特性儲存,若想要取得該函式,就可以透過 Symbol.iterator 符號作為特性取得,例如:

> let arr = [1, 2, 3];
undefined
> let iterator = arr[Symbol.iterator]();
undefined
> iterator.next();
{ value: 1, done: false }
> iterator.next();
{ value: 2, done: false }
> iterator.next();
{ value: 3, done: false }
> iterator.next();
{ value: undefined, done: true }
>

反過來說,如果物件上打算讓它有個函式(方法)可傳回迭代器,在遵守 ES6 的規範下,該函式也要以 Symbol.iterator 符號作為特性儲存,有關迭代器在這方面的細節,之後會有另一份文件介紹。

如果你有個符號,可以使用 Symbol.keyFor 傳回它在全域符號註冊表中的鍵,由於鍵就是符號的描述字串,Symbol.keyFor 實際上只是用來確定符號是否存在於全域符號註冊表之中,如果傳回字串就表示有,傳回 undefined 就表示沒有,例如:

> let s1 = Symbol.for('s1 symbol');
undefined
> Symbol.keyFor(s1);
's1 symbol'
> Symbol.keyFor(Symbol.iterator);
undefined
>

更多符號的實際應用,在之後適當的文件主題之下,會再做說明。