JavaScript/Well-Known Symbols
Well-Known Symbols は、JavaScriptの仕様(ECMAScript)において、特定のアルゴリズムや動作をカスタマイズするために使用される組み込みの Symbol
値です。これらは、オブジェクトのプロパティキーとして使用され、言語の標準的な動作を拡張または変更するための「フック」として機能します。
Well-Known Symbols はすべてのレルム(実行環境)で共有されます。つまり、異なる実行環境(例えば、異なるiframeやWorker)間でも同じ Symbol
値が使用されます。
Well-Known Symbols の一覧
以下の表は、仕様書で定義されている Well-Known Symbols の一覧です。各シンボルは、特定の目的や動作をカスタマイズするために使用されます。
Specification Name | Description | Value and Purpose |
%Symbol.asyncIterator% | Symbol.asyncIterator | オブジェクトのデフォルトの非同期イテレータを返すメソッド。for-await-of ステートメントで使用される。 |
%Symbol.hasInstance% | Symbol.hasInstance | コンストラクタがオブジェクトをそのインスタンスとして認識するかどうかを判定するメソッド。instanceof 演算子で使用される。 |
%Symbol.isConcatSpreadable% | Symbol.isConcatSpreadable | オブジェクトが Array.prototype.concat で配列要素に展開されるかどうかを示すブール値プロパティ。 |
%Symbol.iterator% | Symbol.iterator | オブジェクトのデフォルトのイテレータを返すメソッド。for-of ステートメントで使用される。 |
%Symbol.match% | Symbol.match | 正規表現が文字列にマッチするかどうかを判定するメソッド。String.prototype.match で使用される。 |
%Symbol.matchAll% | Symbol.matchAll | 正規表現が文字列にマッチする結果を返すイテレータを返すメソッド。String.prototype.matchAll で使用される。 |
%Symbol.replace% | Symbol.replace | 文字列のマッチした部分を置換するメソッド。String.prototype.replace で使用される。 |
%Symbol.search% | Symbol.search | 正規表現が文字列にマッチするインデックスを返すメソッド。String.prototype.search で使用される。 |
%Symbol.species% | Symbol.species | 派生オブジェクトを作成するためのコンストラクタ関数を指定するプロパティ。 |
%Symbol.split% | Symbol.split | 正規表現に基づいて文字列を分割するメソッド。String.prototype.split で使用される。 |
%Symbol.toPrimitive% | Symbol.toPrimitive | オブジェクトをプリミティブ値に変換するメソッド。ToPrimitive 抽象操作で使用される。 |
%Symbol.toStringTag% | Symbol.toStringTag | オブジェクトのデフォルトの文字列表現をカスタマイズするための文字列値プロパティ。Object.prototype.toString で使用される。 |
%Symbol.unscopables% | Symbol.unscopables | with ステートメントの環境バインディングから除外するプロパティ名を指定するオブジェクト値プロパティ。 |
各 Well-Known Symbol の詳細
Symbol.asyncIterator
- 非同期イテレータを返すメソッドを定義します。
for-await-of
ループで使用されます。 - 例:
const asyncIterable = { [Symbol.asyncIterator]: async function* () { yield "Hello"; yield "World"; } }; (async () => { for await (const value of asyncIterable) { console.log(value); // "Hello", "World" } })();
- 非同期イテレータを返すメソッドを定義します。
Symbol.hasInstance
instanceof
演算子の動作をカスタマイズします。- 例:
class MyClass { static [instance Symbol.hasInstance] { return Array.isArray(instance); } } console.log([] instanceof MyClass); // true
Symbol.isConcatSpreadable
Array.prototype.concat
でオブジェクトが展開されるかどうかを制御します。- 例:
const arr = [1, 2]; arr[Symbol.isConcatSpreadable] = false; console.log([].concat(arr)); // [[1, 2]]
Symbol.iterator
- イテレータを返すメソッドを定義します。
for-of
ループで使用されます。 - 例:
const iterable = { [Symbol.iterator]: function* () { yield 1; yield 2; yield 3; } }; for (const value of iterable) { console.log(value); // 1, 2, 3 }
- イテレータを返すメソッドを定義します。
Symbol.match
String.prototype.match
の動作をカスタマイズします。- 例:
const customMatcher = { [str Symbol.match] { return str.includes("custom"); } }; console.log("This is a custom string".match(customMatcher)); // true
Symbol.matchAll
String.prototype.matchAll
の動作をカスタマイズします。- 例:
const customMatcher = { [str Symbol.matchAll] { return str.match(/c/g); } }; console.log([..."custom".matchAll(customMatcher)]); // ["c"]
Symbol.replace
String.prototype.replace
の動作をカスタマイズします。- 例:
const customReplacer = { [str, replacement Symbol.replace] { return str.split(" ").join(replacement); } }; console.log("Hello world".replace(customReplacer, "-")); // "Hello-world"
Symbol.search
String.prototype.search
の動作をカスタマイズします。- 例:
const customSearcher = { [str Symbol.search] { return str.indexOf("custom"); } }; console.log("This is a custom string".search(customSearcher)); // 10
Symbol.species
- 派生オブジェクトを作成するためのコンストラクタを指定します。
- 例:
class MyArray extends Array { static get [Symbol.species]() { return Array; } } const myArray = new MyArray(1, 2, 3); const mappedArray = myArray.map(x => x * 2); console.log(mappedArray instanceof MyArray); // false console.log(mappedArray instanceof Array); // true
Symbol.split
String.prototype.split
の動作をカスタマイズします。- 例:
const customSplitter = { [str Symbol.split] { return str.split(" "); } }; console.log("Hello world".split(customSplitter)); // ["Hello", "world"]
Symbol.toPrimitive
- オブジェクトをプリミティブ値に変換する動作をカスタマイズします。
- 例:
const obj = { [hint Symbol.toPrimitive] { if (hint === "number") { return 42; } if (hint === "string") { return "forty-two"; } return true; } }; console.log(+obj); // 42 console.log(`${obj}`); // "forty-two" console.log(obj == true); // true
Symbol.toStringTag
- オブジェクトのデフォルトの文字列表現をカスタマイズします。
- 例:
const obj = { [Symbol.toStringTag]: "MyObject" }; console.log(obj.toString()); // [object MyObject]
Symbol.unscopables
with
ステートメントの環境バインディングから除外するプロパティを指定します。- 例:
const obj = { foo: 1, bar: 2, [Symbol.unscopables]: { foo: true } }; with (obj) { console.log(bar); // 2 console.log(foo); // ReferenceError: foo is not defined }
歴史
Well-Known Symbols は、JavaScript(ECMAScript)の進化とともに導入され、言語の拡張性と柔軟性を高めるために重要な役割を果たしてきました。その歴史は、主に ECMAScript のバージョンアップに伴って進化してきました。以下に、Well-Known Symbols の歴史をまとめます。
1. ECMAScript 2015 (ES6) での導入
Well-Known Symbols が初めて導入されたのは、ECMAScript 2015(ES6) です。ES6 は、JavaScript の大幅なアップデートであり、多くの新機能が追加されました。その中で、Symbol
型と Well-Known Symbols が導入され、オブジェクトの動作をカスタマイズするための標準的な方法が提供されました。
ES6 で導入された Well-Known Symbols
Symbol.iterator
Symbol.hasInstance
Symbol.isConcatSpreadable
Symbol.match
Symbol.replace
Symbol.search
Symbol.species
Symbol.split
Symbol.toPrimitive
Symbol.toStringTag
Symbol.unscopables
これらのシンボルは、JavaScript の標準的な動作(例えば、イテレーション、型変換、正規表現操作など)をカスタマイズするために使用されます。
背景
ES6 以前は、JavaScript の標準的な動作をカスタマイズする方法が限られていました。例えば、toString
や valueOf
のようなメソッドは存在しましたが、それらは限定的で、すべてのユースケースに対応できませんでした。Well-Known Symbols の導入により、開発者は言語の動作をより柔軟にカスタマイズできるようになりました。
2. ECMAScript 2016 (ES7) での追加
ES6 の後、ECMAScript のアップデートは毎年行われ、小さな改良が加えられました。ECMAScript 2016(ES7) では、新しい Well-Known Symbol は追加されませんでしたが、既存のシンボルの使用法がさらに広がりました。
3. ECMAScript 2018 (ES9) での追加
ECMAScript 2018(ES9) では、新しい Well-Known Symbol として Symbol.asyncIterator
が追加されました。これは、非同期イテレーションをサポートするためのものです。
Symbol.asyncIterator
の役割
- 非同期イテレータを返すメソッドを定義します。
for-await-of
ループで使用されます。
例
const asyncIterable = { [Symbol.asyncIterator]: async function* () { yield "Hello"; yield "World"; } }; (async () => { for await (const value of asyncIterable) { console.log(value); // "Hello", "World" } })();
この追加により、非同期処理とイテレーションを組み合わせた新しいパターンが可能になりました。
4. ECMAScript 2020 (ES11) での追加
ECMAScript 2020(ES11) では、新しい Well-Known Symbol として Symbol.matchAll
が追加されました。これは、正規表現のマッチ結果をイテレータとして返すためのものです。
Symbol.matchAll
の役割
- 正規表現が文字列にマッチする結果をイテレータとして返します。
String.prototype.matchAll
で使用されます。
例
const regex = /t(e)(st(\d?))/g; const str = 'test1test2'; const matches = str.matchAll(regex); for (const match of matches) { console.log(match); } // ["test1", "e", "st1", "1"] // ["test2", "e", "st2", "2"]
この追加により、正規表現のマッチ結果をより柔軟に扱えるようになりました。
5. 現在の状況
2025年2月現在、Well-Known Symbols は以下の13種類が定義されています。
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
これらのシンボルは、JavaScript の標準的な動作をカスタマイズするための強力なツールとして広く利用されています。
歴史的な背景と意義
Well-Known Symbols の導入は、JavaScript の拡張性を大幅に向上させました。以下にその意義をまとめます。
- 標準化されたカスタマイズ方法の提供
- 以前は、特定の動作をカスタマイズするために非標準的な方法(例えば、
toString
やvalueOf
のオーバーライド)に頼る必要がありました。Well-Known Symbols の導入により、標準化された方法で動作をカスタマイズできるようになりました。
- 以前は、特定の動作をカスタマイズするために非標準的な方法(例えば、
- 言語の柔軟性の向上
- Well-Known Symbols により、開発者は言語の動作をより細かく制御できるようになりました。例えば、イテレーション、型変換、正規表現操作など、さまざまな動作をカスタマイズできます。
- 非同期処理のサポート
Symbol.asyncIterator
の導入により、非同期処理とイテレーションを組み合わせた新しいパターンが可能になりました。
- 後方互換性の維持
- Well-Known Symbols は、既存のコードに影響を与えずに新しい機能を追加するための方法として設計されています。これにより、後方互換性を維持しながら言語を進化させることが可能になりました。
Well-Known Symbols は、ECMAScript 2015(ES6)で初めて導入され、その後も ECMAScript の進化に伴って新しいシンボルが追加されてきました。これらのシンボルは、JavaScript の標準的な動作をカスタマイズするための強力なツールとして、言語の拡張性と柔軟性を大幅に向上させました。今後も、JavaScript の進化に伴って新しい Well-Known Symbols が追加される可能性があります。
まとめ
Well-Known Symbols は、JavaScriptの標準的な動作をカスタマイズするための強力なツールです。これらを使用することで、オブジェクトの振る舞いを柔軟に変更したり、特定の操作をフックしたりすることができます。各シンボルは、特定の目的に特化しており、言語の拡張性を高める役割を果たしています。