Symbolについて
概要
ES2015ではUndefined、Null、Boolean、Number、String、Objectの6つの型に加えて、新しい型Symbolが導入された。
シンボルはプリミティブ型で、文字列のようにプロパティのキーとして使える特徴を持つ。
振る舞い
シンボルはSymbolコンストラクタを呼ぶことで作られ、その時に引数として文字列を渡せば名前を持たせることができる。
sym1 = Symbol()
sym2 = Symbol('name')
typeof演算子で評価すると"symbol"が返される。
typeof sym1 // "symbol"
Symbolコンストラクタをnew付きで呼び出すことは出来ないが、シンボルをObjectコンストラクタに渡すことでラップすることはできる。
new Symbol() // TypeError symobj = Object(sym2) typeof symobj // "object"
各シンボルはユニークであり、原則他のシンボルと同じものを作り出すことはできない。
sym1 === sym1 // true Symbol('abc') === Symbol('abc') // false
Symbol.prototype.valueOf は自分自身のシンボルを返す。
Symbol.prototype.toString は名前が含まれた表記を返す。
sym1 === sym1.valueOf() // true sym2 === symobj.valueOf() // true sym2 === symobj // true sym1.toString() // "Symbol()" sym2.toString() // "Symbol(name)"
但しシンボルやシンボルオブジェクトを数値型や、暗黙的に文字列型に変換しようとするとエラーになる。
真偽値として評価するとtrueとして扱われる。
String(sym2) // "Symbol(name)" sym2.toString() // "Symbol(name)" '' + sym2 // TypeError Number(sym2) // TypeError +sym2 // TypeError Boolean(sym2) // true !!sym2 // true
プロパティのキーとして使う
シンボルは文字列と同じようにプロパティのキーとして使える。
obj = {} obj[sym1] = 123 obj[sym1] * 2 // 246
但し文字列と違ってそのシンボルを直接持っていないとプロパティの値にアクセスできないので、表に出したくない(プライベートっぽい)メンバを実現するのに使える。
// 文字列キーを使った場合 A = function () { var n = 1 function A() { this._id = n++ } A.prototype.getId = function () { return this._id } return A }() a = new A // 簡単にアクセスできるし、簡単に列挙されてしまう、何より見栄えが悪い a._id // 1 Object.keys(a) // ["_id"]
// シンボルキーを使った場合 B = function () { var idsym = Symbol('id') var n = 1 function B() { this[idsym] = n++ } B.prototype.getId = function () { return this[idsym] } return B }() b = new B // 隠蔽度が高い b._id // undefined Object.keys(b) // [] // 完全に隠蔽されるわけではない idsym = Object.getOwnPropertySymbols(b)[0] // Symbol(id) b[idsym] // 1
シンボルを登録する
前記したように、シンボルはユニークであるが、異なる場所間で同一のシンボルを共有したい場合がある。
sym1 = Symbol('abc') sym2 = Symbol('abc') sym1 === sym2 // false // 当然一致しない
その場合にSymbol.keyを使うことで、初回は文字列に紐付けられた新しいシンボルが返される。
そしてその後に同じキーでSymbol.forされると、登録されたシンボルが返される。
sym1 = Symbol.for('abc') // 新しく作られ、登録される sym2 = Symbol.for('abc') // 登録されたものが返される sym3 = Symbol('abc') // ユニークなシンボル sym1 === sym2 // true sym2 === sym3 // false
グローバルが異なる環境同士でも問題なく共有することができる。
sym1 = thisGlobal.Symbol.for('abc') sym2 = otherGlobal.Symbol.for('abc') sym1 === sym2 // true
Symbol.keyForでシンボルがどういう文字列に紐付けられているかを確認できる。
sym1 = Symbol.for('abc') sym2 = Symbol('abc') Symbol.keyFor(sym1) // "abc" Symbol.keyFor(sym2) // undefined
ビルトインシンボルについて
ES2015では特別な意味を持つシンボルがいくつか定義されており、それらのシンボルプロパティをオブジェクトに作っておくと、特定の場面で参照され値が利用される。
例:@@iterator(="iterator"シンボル)
// ビルトインシンボルはSymbolオブジェクトに生えているので、それを貰う iteratorSymbol = Symbol.iterator obj = { a: 1, b: 2, c: 3 } // オブジェクトに"iterator"シンボルプロパティを設定する obj[iteratorSymbol] = function () { return Object.keys(this).values() } // これはイテレータが期待される場面で利用される(↓リンク先参照)
実装済のビルトインシンボル
- @@iterator
- @@unscopables
- @@toStringTag
- @@isConcatSpreadable
- @@toPrimitive
- @@match @@replace @@search @@split
- @@species
- @@hasInstance
実装されたバージョン
V8 3.17.7 3.25.1(修正) 3.25.23(symbol registry) 3.28.26(修正) 3.28.59(デフォルト有効) 3.28.71(修正) 3.29.61(修正:「String(sym)」は「TypeError」にならない)
Chrome 38M(デフォルト有効)