シンボルとは
- 定数みたいなもの
- 他のシンボルと等値比較できて、他のどのシンボルとも異なる(ユニークである)
- オブジェクトのキーにできる
- 一旦、ユニークな文字列だと思ってもらっても良い
const symbol1 = Symbol(); // シンボルを作成
const symbol2 = Symbol(); // 別のシンボルを作成
console.log(symbol1 === symbol2); // => false
const a = {};
a[symbol1] = 10;
a[symbol2] = 20;
console.log(a); // => {Symbol(): 10, Symbol(): 20}
console.log(a[symbol1]); // => 10
console.log(a[symbol2]); // => 20
Symbol.split とは
- JavaScript に存在する定数
- これ単体ではあまり意味がない
a[Symbol.split] = 30;
console.log(a); // => {Symbol(): 10, Symbol(): 20, Symbol(Symbol.split): 30}
console.log(a[Symbol.split]); // => 30
正規表現オブジェクトの Symbol.split メソッドとは
- 正規表現オブジェクトは Symbol.split をキーとする項目を持つ
- これは関数オブジェクト
- 正確には正規表現のプロトタイプが持っている
const regexp = /[xyz]+/;
console.log(regexp[Symbol.split]); // => ƒ [Symbol.split]() { [native code] }
その関数は 2 つの引数を取り、配列を返す
RegExp.prototype[Symbol.split]()
は 2 つの引数を取る関数になっている
const regexp = /[xyz]+/;
// 引数は 2 個
console.log(regexp[Symbol.split].length); // => 2
// 最初の引数は split 対象の文字列
console.log(regexp[Symbol.split]("123xyz456zyx789")); // => ['123', '456', '789']
「Symbol.split メソッドを持つオブジェクト」とは
- オブジェクトであって、
- かつ Symbol.split をキーとするメソッドを持つもの
つまり、こういうオブジェクトです。
const obj = {};
obj[Symbol.split] = () => "Hey! Listen!";
// またはこのようにも書ける
// const obj = {
// [Symbol.split]: () => "Hey! Listen!",
// };
console.log(typeof(obj)); // => object
console.log(obj[Symbol.split]); // => () => "Hey! Listen!"
つまり、 String.prototype.split()
の引数に Symbol.split メソッドを持つオブジェクト
を指定するということは、こういうことになります。
const obj = {};
obj[Symbol.split] = () => "Hey! Listen!";
"abc123".split(obj); // => 'Hey! Listen!'
separator
各分割がどこで行われるかを表すパターンです。undefined、文字列、または Symbol.split メソッドを持つオブジェクトを指定することができます。典型的な例は正規表現です。
まとめ
-
Symbol.split
というシンボルが JavaScript に存在する -
String.prototype.split()
の引数にSymbol.split メソッドを持つオブジェクト
を指定することができる - 正規表現オブジェクトは
Symbol.split
メソッドを持つconsole.log((/[xyz]+/)[Symbol.split]); // => 関数オブジェクト
- つまり、
String.prototype.split()
の引数に正規表現オブジェクトを指定することができる -
Symbol.split
は、正規表現オブジェクト等にこの関数を備えるためのオブジェクトのキー
余談(なぜシンボルは文字列ではないのか)
- シンボルを
Symbol
という名前空間に定義することで、既存のコードを破壊せずに済む - もし正規表現オブジェクトに ES6 側で
split
というプロパティを生やしてしまうと、既存のコードが壊れてしまう- このため、シンボルは文字列とは異なるものである必要がある
const regex = /[xyz]+/;
// もしシンボルと文字列が同じなら、以下の ES6 以前のコードが壊れる
regex["split"] = "Hello";
console.log(regex["split"]); // => Hello
// シンボルは他のどのシンボルとも異なるため、この ES6 以前のコードは壊れない
const Symbol = { split: "myKey" };
regex[Symbol.split] = "World";
console.log(regex[Symbol.split]); // => World
- また、
for ... in
などの挙動が壊れないように、シンボルがキーの項目は列挙されないようになっている
const a = {
foo: 10, // キーが文字列
[Symbol()]: 20, // キーがシンボル
}
for (const x in a) {
console.log(x); // foo だけ列挙される
}
- このように、互換性の都合が大きいため、一般 Web 開発ユーザーにとっては Symbol を使う機会はほとんどないかもしれない
感想
JavaScript のプロトタイプには、特別なシンボルをキーに持つインスタンスメソッドや静的プロパティがたくさんあるようです。勉強になりました。
- Symbol.asyncIterator
- Symbol.hasInstance
- Symbol.isConcatSpreadable
- Symbol.iterator
- Symbol.match
- Symbol.matchAll
- Symbol.replace
- Symbol.search
- Symbol.species
- Symbol.split
- Symbol.toPrimitive
- Symbol.toStringTag
- Symbol.unscopables