7. 記号
目次
本書をサポートしてください:購入 (PDF, EPUB, MOBI) または 寄付
(広告、ブロックしないでください。)

7. 記号



7.1 概要

記号はECMAScript 6の新しいプリミティブ型です。ファクトリ関数によって作成されます。

const mySymbol = Symbol('mySymbol');

ファクトリ関数を呼び出すたびに、新しく一意の記号が作成されます。オプションのパラメーターは、記号を出力表示するときに表示される記述的な文字列です(他の目的はありません)。

> mySymbol
Symbol(mySymbol)

7.1.1 ユースケース1:一意のプロパティキー

記号は主に一意のプロパティキーとして使用されます。記号は他のプロパティキー(記号または文字列)と決して衝突しません。たとえば、Symbol.iteratorに格納されている記号をメソッドのキーとして使用することにより、オブジェクトを反復可能for-ofループやその他の言語メカニズムで使用可能)にすることができます(反復可能オブジェクトの詳細については、反復に関する章を参照してください)。

const iterableObject = {
    [Symbol.iterator]() { // (A)
        ···
    }
}
for (const x of iterableObject) {
    console.log(x);
}
// Output:
// hello
// world

行Aでは、記号がメソッドのキーとして使用されています。この一意のマーカーにより、オブジェクトが反復可能になり、for-ofループを使用できるようになります。

7.1.2 ユースケース2:概念を表す定数

ECMAScript 5では、色などの概念を表すために文字列を使用していた可能性があります。ES6では、記号を使用することで、それらが常に一意であることを確認できます。

const COLOR_RED    = Symbol('Red');
const COLOR_ORANGE = Symbol('Orange');
const COLOR_YELLOW = Symbol('Yellow');
const COLOR_GREEN  = Symbol('Green');
const COLOR_BLUE   = Symbol('Blue');
const COLOR_VIOLET = Symbol('Violet');

function getComplement(color) {
    switch (color) {
        case COLOR_RED:
            return COLOR_GREEN;
        case COLOR_ORANGE:
            return COLOR_BLUE;
        case COLOR_YELLOW:
            return COLOR_VIOLET;
        case COLOR_GREEN:
            return COLOR_RED;
        case COLOR_BLUE:
            return COLOR_ORANGE;
        case COLOR_VIOLET:
            return COLOR_YELLOW;
        default:
            throw new Exception('Unknown color: '+color);
    }
}

Symbol('Red')を呼び出すたびに、新しい記号が作成されます。したがって、COLOR_REDは他の値と間違えられることはありません。文字列'Red'の場合とは異なります。

7.1.3 陥りやすい点:記号を文字列に強制変換できません

記号を文字列に強制変換(暗黙的に変換)すると、例外がスローされます。

const sym = Symbol('desc');

const str1 = '' + sym; // TypeError
const str2 = `${sym}`; // TypeError

唯一の解決策は、明示的に変換することです。

const str2 = String(sym); // 'Symbol(desc)'
const str3 = sym.toString(); // 'Symbol(desc)'

強制変換を禁止することで、いくつかのエラーを防ぐことができますが、記号の操作も複雑になります。

次の操作は、プロパティキーとしての記号を認識します。

次の操作は、プロパティキーとしての記号を無視します。

7.2 新しいプリミティブ型

ECMAScript 6では、新しいプリミティブ型である記号が導入されました。それらは、一意のIDとして機能するトークンです。ファクトリ関数Symbol()を使用して記号を作成します(これは、関数として呼び出された場合に文字列を返すStringと大まかに似ています)。

const symbol1 = Symbol();

Symbol()には、オプションの文字列値のパラメーターがあり、新しく作成された記号に説明を与えることができます。その説明は、記号が文字列に変換される(toString()またはString()を介して)場合に使用されます。

> const symbol2 = Symbol('symbol2');
> String(symbol2)
'Symbol(symbol2)'

Symbol()によって返されるすべての記号は一意であり、すべての記号は独自のアイデンティティを持っています。

> Symbol() === Symbol()
false

typeof演算子を記号のいずれかに適用すると、プリミティブであることがわかります。新しい記号固有の結果を返します。

> typeof Symbol()
'symbol'

7.2.1 プロパティキーとしての記号

記号はプロパティキーとして使用できます。

const MY_KEY = Symbol();
const obj = {};

obj[MY_KEY] = 123;
console.log(obj[MY_KEY]); // 123

クラスとオブジェクトリテラルには、計算されたプロパティキーと呼ばれる機能があります。式を角括弧で囲むことで、プロパティのキーを式で指定できます。次のオブジェクトリテラルでは、計算されたプロパティキーを使用して、MY_KEYの値をプロパティのキーにします。

const MY_KEY = Symbol();
const obj = {
    [MY_KEY]: 123
};

メソッド定義にも計算されたキーを持つことができます。

const FOO = Symbol();
const obj = {
    [FOO]() {
        return 'bar';
    }
};
console.log(obj[FOO]()); // bar

7.2.2 独自のプロパティキーの列挙

プロパティのキーになることができる新しい種類の値が導入されたため、ECMAScript 6では次の用語が使用されます。

まずオブジェクトを作成することで、独自のプロパティキーを列挙するためのAPIを調べます。

const obj = {
    [Symbol('my_key')]: 1,
    enum: 2,
    nonEnum: 3
};
Object.defineProperty(obj,
    'nonEnum', { enumerable: false });

Object.getOwnPropertyNames()は、記号値のプロパティキーを無視します。

> Object.getOwnPropertyNames(obj)
['enum', 'nonEnum']

Object.getOwnPropertySymbols()は、文字列値のプロパティキーを無視します。

> Object.getOwnPropertySymbols(obj)
[Symbol(my_key)]

Reflect.ownKeys()は、あらゆる種類のキーを考慮します。

> Reflect.ownKeys(obj)
[Symbol(my_key), 'enum', 'nonEnum']

Object.keys()は、文字列である列挙可能なプロパティキーのみを考慮します。

> Object.keys(obj)
['enum']

名前Object.keysは新しい用語と衝突します(文字列キーのみがリストされます)。Object.namesまたはObject.getEnumerableOwnPropertyNamesの方が、現在ではより良い選択です。

7.3 概念を表す記号の使用

ECMAScript 5では、多くの場合、概念(列挙型定数を考えてください)を文字列で表します。たとえば

var COLOR_RED    = 'Red';
var COLOR_ORANGE = 'Orange';
var COLOR_YELLOW = 'Yellow';
var COLOR_GREEN  = 'Green';
var COLOR_BLUE   = 'Blue';
var COLOR_VIOLET = 'Violet';

しかし、文字列は私たちが望むほど一意ではありません。その理由を理解するために、次の関数を見てみましょう。

function getComplement(color) {
    switch (color) {
        case COLOR_RED:
            return COLOR_GREEN;
        case COLOR_ORANGE:
            return COLOR_BLUE;
        case COLOR_YELLOW:
            return COLOR_VIOLET;
        case COLOR_GREEN:
            return COLOR_RED;
        case COLOR_BLUE:
            return COLOR_ORANGE;
        case COLOR_VIOLET:
            return COLOR_YELLOW;
        default:
            throw new Exception('Unknown color: '+color);
    }
}

任意の式をswitchケースとして使用できることに注意してください。何らかの制限はありません。たとえば

function isThree(x) {
    switch (x) {
        case 1 + 1 + 1:
            return true;
        default:
            return false;
    }
}

switchが提供する柔軟性を活用し、ハードコーディング('Red'など)ではなく、定数(COLOR_REDなど)を介して色を参照します。

興味深いことに、そうしたとしても、まだ混同が生じる可能性があります。たとえば、誰かが気分の定数を定義するかもしれません。

var MOOD_BLUE = 'Blue';

これでCOLOR_BLUEの値はもはや一意ではなくなり、MOOD_BLUEと間違えられる可能性があります。getComplement()のパラメーターとして使用すると、例外をスローするべきところ'Orange'を返します。

この例を修正するために記号を使用しましょう。これで、ES6の機能constも使用できます。これにより、実際の定数を宣言できます(定数にバインドされている値を変更することはできませんが、値自体は変更可能である可能性があります)。

const COLOR_RED    = Symbol('Red');
const COLOR_ORANGE = Symbol('Orange');
const COLOR_YELLOW = Symbol('Yellow');
const COLOR_GREEN  = Symbol('Green');
const COLOR_BLUE   = Symbol('Blue');
const COLOR_VIOLET = Symbol('Violet');

Symbolによって返される各値は一意であるため、他の値がBLUEと間違えられることはありません。興味深いことに、文字列の代わりに記号を使用した場合でも、getComplement()のコードはまったく変化しません。これは、それらがいかに類似しているかを示しています。

7.4 プロパティのキーとしての記号

他のキーと決して衝突しないキーを持つプロパティを作成できることは、次の2つの状況で役立ちます。

7.4.1 非公開プロパティのキーとしての記号

JavaScriptに継承階層がある場合(たとえば、クラス、mixin、または純粋にプロトタイプベースのアプローチによって作成された場合)、2種類のプロパティがあります。

使いやすさのために、公開プロパティは通常、文字列キーを持ちます。しかし、文字列キーを持つプライベートプロパティでは、意図しない名前の衝突が問題になる可能性があります。そのため、記号が良い選択です。たとえば、次のコードでは、プライベートプロパティ_counter_actionに記号が使用されています。

const _counter = Symbol('counter');
const _action = Symbol('action');
class Countdown {
    constructor(counter, action) {
        this[_counter] = counter;
        this[_action] = action;
    }
    dec() {
        let counter = this[_counter];
        if (counter < 1) return;
        counter--;
        this[_counter] = counter;
        if (counter === 0) {
            this[_action]();
        }
    }
}

記号は名前の衝突からしか保護されず、許可されていないアクセスからは保護されないことに注意してください。なぜなら、Reflect.ownKeys()を使用して、オブジェクトのすべての独自のキー(記号を含む)を見つけることができるからです。そこでも保護が必要な場合は、「クラスのプライベートデータ」でリストされているアプローチのいずれかを使用できます。

7.4.2 メタレベルプロパティのキーとしての記号

一意のアイデンティティを持つ記号は、「通常の」プロパティキーとは異なるレベルにある公開プロパティのキーとして理想的です。なぜなら、メタレベルキーと通常のキーは衝突してはならないからです。メタレベルプロパティの1つの例は、オブジェクトがライブラリによって処理される方法をカスタマイズするために実装できるメソッドです。記号キーを使用すると、ライブラリが通常のメソッドをカスタマイズメソッドと間違えるのを防ぎます。

ES6の反復可能性はそのようなカスタマイズの1つです。オブジェクトは、キーが記号(に格納されている)Symbol.iteratorであるメソッドを持っている場合、反復可能です。次のコードでは、objは反復可能です。

const obj = {
    data: [ 'hello', 'world' ],
    [Symbol.iterator]() {
        ···
    }
};

objの反復可能性により、for-ofループや同様のJavaScript機能を使用できます。

for (const x of obj) {
    console.log(x);
}

// Output:
// hello
// world

7.4.3 JavaScript標準ライブラリの名前衝突の例

名前の衝突は重要ではないと思われる場合に備えて、JavaScript標準ライブラリの進化において名前の衝突が問題を引き起こした3つの例を次に示します。

対照的に、プロパティキーSymbol.iteratorを使用してオブジェクトに反復可能性を追加することは、そのキーが他のものと競合しないため、問題を引き起こすことはありません。

7.5 シンボルを他のプリミティブ型に変換する

次の表は、シンボルを明示的または暗黙的に他のプリミティブ型に変換した場合に何が起こるかを示しています。

変換先 明示的変換 強制型変換(暗黙的変換)
boolean Boolean(sym) → OK !sym → OK
number Number(sym)TypeError sym*2TypeError
string String(sym) → OK ''+symTypeError
  sym.toString() → OK `${sym}`TypeError

7.5.1 落とし穴:文字列への強制型変換

文字列への強制型変換が禁止されていることは、簡単に落とし穴になります。

const sym = Symbol();

console.log('A symbol: '+sym); // TypeError
console.log(`A symbol: ${sym}`); // TypeError

これらの問題を解決するには、文字列への明示的な変換が必要です。

console.log('A symbol: '+String(sym)); // OK
console.log(`A symbol: ${String(sym)}`); // OK

7.5.2 強制型変換ルールの意味

強制型変換(暗黙的変換)は、シンボルでは多くの場合禁止されています。このセクションでは、その理由を説明します。

7.5.2.1 真偽値チェックは許可される

booleanへの強制型変換は常に許可されており、主にif文やその他の場所で真偽値チェックを有効にするためです。

if (value) { ··· }

param = param || 0;
7.5.2.2 シンボルをプロパティキーに誤って変換する

シンボルは特別なプロパティキーであるため、文字列(異なる種類のプロパティキー)への誤った変換を避ける必要があります。これは、加算演算子を使用してプロパティの名前を計算する場合に発生する可能性があります。

myObject['__' + value]

そのため、valueがシンボルの場合、TypeErrorがスローされます。

7.5.2.3 シンボルを配列インデックスに誤って変換する

シンボルを配列インデックスに誤って変換することも避けたいです。以下は、valueがシンボルの場合に発生する可能性のあるコードです。

myArray[1 + value]

そのため、この場合、加算演算子はエラーをスローします。

7.5.3 仕様における明示的および暗黙的変換

7.5.3.1 booleanへの変換

シンボルをbooleanに明示的に変換するには、Boolean()を呼び出します。これは、シンボルに対してtrueを返します。

> const sym = Symbol('hello');
> Boolean(sym)
true

Boolean()は、内部操作ToBoolean()を介して結果を計算します。これは、シンボルおよびその他の真偽値に対してtrueを返します。

強制型変換もToBoolean()を使用します。

> !sym
false
7.5.3.2 numberへの変換

シンボルをnumberに明示的に変換するには、Number()を呼び出します。

> const sym = Symbol('hello');
> Number(sym)
TypeError: can't convert symbol to number

Number()は、内部操作ToNumber()を介して結果を計算します。これは、シンボルに対してTypeErrorをスローします。

強制型変換もToNumber()を使用します。

> +sym
TypeError: can't convert symbol to number
7.5.3.3 stringへの変換

シンボルをstringに明示的に変換するには、String()を呼び出します。

> const sym = Symbol('hello');
> String(sym)
'Symbol(hello)'

String()のパラメータがシンボルの場合、それは文字列への変換を自身で処理し、シンボルの作成時に提供された記述をラップした文字列Symbol()を返します。記述が提供されていない場合、空文字列が使用されます。

> String(Symbol())
'Symbol()'

toString()メソッドはString()と同じ文字列を返しますが、これらの2つの操作のいずれも他方を呼び出すことはありません。どちらも同じ内部操作SymbolDescriptiveString()を呼び出します。

> Symbol('hello').toString()
'Symbol(hello)'

強制型変換は内部操作ToString()を介して処理されます。これは、シンボルに対してTypeErrorをスローします。パラメータを文字列に変換するメソッドの1つはNumber.parseInt()です。

> Number.parseInt(Symbol())
TypeError: can't convert symbol to string
7.5.3.4 許可されない:二項加算演算子(+)による変換

加算演算子は次のように機能します。

文字列または数値への強制型変換は例外をスローするため、(直接)シンボルに対して加算演算子を使用することはできません。

> '' + Symbol()
TypeError: can't convert symbol to string
> 1 + Symbol()
TypeError: can't convert symbol to number

7.6 シンボルのラッパーオブジェクト

他のすべてのプリミティブ値にはリテラルがありますが、関数呼び出しSymbolによってシンボルを作成する必要があります。したがって、Symbolをコンストラクタとして誤って呼び出すリスクがあります。これはSymbolのインスタンスを生成しますが、あまり役に立ちません。そのため、そうしようとすると例外がスローされます。

> new Symbol()
TypeError: Symbol is not a constructor

それでも、ラッパーオブジェクト(Symbolのインスタンス)を作成する方法があります。関数として呼び出されたObjectは、シンボルを含むすべての値をオブジェクトに変換します。

> const sym = Symbol();
> typeof sym
'symbol'

> const wrapper = Object(sym);
> typeof wrapper
'object'
> wrapper instanceof Symbol
true

7.6.1 [ ]とラップされたキーによるプロパティへのアクセス

角括弧演算子[ ]は通常、そのオペランドを文字列に変換します。現在、2つの例外があります。シンボルのラッパーオブジェクトはアンラップされ、シンボルはそのままで使用されます。この現象を調べるために、次のオブジェクトを使用します。

const sym = Symbol('yes');
const obj = {
    [sym]: 'a',
    str: 'b',
};

角括弧演算子は、ラップされたシンボルをアンラップします。

> const wrappedSymbol = Object(sym);
> typeof wrappedSymbol
'object'
> obj[wrappedSymbol]
'a'

シンボルに関連しない他の値と同様に、ラップされた文字列は角括弧演算子によって文字列に変換されます。

> const wrappedString = new String('str');
> typeof wrappedString
'object'
> obj[wrappedString]
'b'
7.6.1.1 仕様におけるプロパティアクセス

プロパティを取得および設定するための演算子は、内部操作ToPropertyKey()を使用します。これは次のように機能します。

7.7 シンボルによるレルム間の移動

コードレルム(略してレルム)とは、コードの一部が存在するコンテキストです。これには、グローバル変数、ロードされたモジュールなどが含まれます。コードは正確に1つのレルム「内」に存在しますが、他のレルムのコードにアクセスできる場合があります。たとえば、ブラウザの各フレームには独自のレルムがあります。そして、次のHTMLが示すように、実行は1つのフレームから別のフレームにジャンプできます。

<head>
    <script>
        function test(arr) {
            var iframe = frames[0];
            // This code and the iframe’s code exist in
            // different realms. Therefore, global variables
            // such as Array are different:
            console.log(Array === iframe.Array); // false
            console.log(arr instanceof Array); // false
            console.log(arr instanceof iframe.Array); // true

            // But: symbols are the same
            console.log(Symbol.iterator ===
                        iframe.Symbol.iterator); // true
        }
    </script>
</head>
<body>
    <iframe srcdoc="<script>window.parent.test([])</script>">
</iframe>
</body>

問題は、各レルムが独自のグローバル変数を持ち、各変数Arrayが異なるオブジェクトを指していることです。それらは本質的に同じオブジェクトであるにもかかわらずです。同様に、ライブラリとユーザーコードはレルムごとに1回ロードされ、各レルムは同じオブジェクトの異なるバージョンを持ちます。

オブジェクトは同一性で比較されますが、ブール値、数値、文字列は値で比較されます。したがって、数値123がどのレルムで生成されたかに関係なく、他のすべての123と区別できません。これは、数値リテラル123が常に同じ値を生成することと似ています。

シンボルは個別の同一性を持つため、他のプリミティブ値ほどスムーズにレルム間を移動しません。これは、すべてのレルムで機能する必要があるSymbol.iteratorなどのシンボルにとって問題です。オブジェクトが1つのレルムで反復可能である場合、すべてのレルムで反復可能である必要があります。すべての組み込みシンボルはJavaScriptエンジンによって管理され、たとえばSymbol.iteratorが各レルムで同じ値であることを確認します。ライブラリがレルム間シンボルを提供したい場合、追加のサポートに依存する必要があります。それはグローバルシンボルレジストリという形で提供されます。このレジストリはすべてのレルムにグローバルであり、文字列をシンボルにマッピングします。各シンボルについて、ライブラリはできるだけ一意な文字列を考案する必要があります。シンボルを作成するために、Symbol()を使用するのではなく、レジストリに文字列がマッピングされているシンボルを要求します。レジストリに既に文字列のエントリがある場合、関連付けられたシンボルが返されます。そうでない場合は、エントリとシンボルが最初に作成されます。

Symbol.for()を介してレジストリにシンボルを要求し、シンボルに関連付けられた文字列(そのキー)をSymbol.keyFor()を介して取得します。

> const sym = Symbol.for('Hello everybody!');
> Symbol.keyFor(sym)
'Hello everybody!'

JavaScriptエンジンによって提供される、Symbol.iteratorなどのレルム間シンボルは、レジストリにはありません。

> Symbol.keyFor(Symbol.iterator)
undefined

7.8 FAQ:シンボル

7.8.1 プライベートプロパティの定義にシンボルを使用できますか?

元の計画では、シンボルを使用してプライベートプロパティをサポートすることでした(公開シンボルとプライベートシンボルがありました)。しかし、プライベートデータの管理に「get」と「set」(2つのメタオブジェクトプロトコル操作)を使用することは、プロキシと適切に連携しません。

この2つの目標は相反しています。クラスに関する章では、プライベートデータの管理方法について説明しています。シンボルはそのオプションの1つですが、プライベートシンボルと同等の安全性を確保できません。これは、Object.getOwnPropertySymbols()Reflect.ownKeys()を使用して、オブジェクトのプロパティキーとして使用されているシンボルを特定できるためです。

7.8.2 シンボルはプリミティブ型ですか、それともオブジェクトですか?

シンボルは、ある意味ではプリミティブ値に似ており、またある意味ではオブジェクトに似ています。

それでは、シンボルとはプリミティブ値ですか、それともオブジェクトですか?最終的に、2つの理由からプリミティブ型になりました。

まず、シンボルはオブジェクトよりも文字列に似ています。言語の基本的な値であり、不変であり、プロパティキーとして使用できます。シンボルが一意のアイデンティティを持つことは、文字列に似ていることと必ずしも矛盾しません。UUIDアルゴリズムは、準一意の文字列を生成します。

第二に、シンボルはプロパティキーとして最も頻繁に使用されるため、JavaScriptの仕様と実装をそのユースケースに合わせて最適化することが理にかなっています。そうすれば、シンボルはオブジェクトの多くの機能を必要としません。

シンボルがこれらの機能を持たないことで、仕様と実装が容易になります。V8チームも、プロパティキーに関して、プリミティブ型を特別なケースにする方が特定のオブジェクトにするよりも簡単であると述べています。

7.8.3 本当にシンボルが必要ですか?文字列だけで十分ではありませんか?

文字列とは対照的に、シンボルは一意であり、名前の衝突を防ぎます。これは、色などのトークンには便利ですが、Symbol.iteratorをキーとするもののようなメタレベルメソッドをサポートするには不可欠です。Pythonは、衝突を避けるために特別な名前__iter__を使用します。プログラミング言語のメカニズムには、ダブルアンダースコアの名前を予約できますが、ライブラリはどうすればよいでしょうか?シンボルを使用すると、誰もが利用できる拡張メカニズムが得られます。後述のパブリックシンボルのセクションでわかるように、JavaScript自体ですでにこのメカニズムを十分に活用しています。

衝突のないプロパティキーに関して、シンボルに代わる仮説的な代替案が1つあります。命名規則を使用することです。たとえば、URLを含む文字列(例:'http://example.com/iterator')。しかし、これにより、プロパティキーの2番目のカテゴリが導入されます(通常は有効な識別子であり、コロン、スラッシュ、ドットなどを含まない「通常の」プロパティ名と対照的です)。これは基本的にシンボルが何であるかと同じです。そうなると、新しい種類の値を導入した方が良いでしょう。

7.8.4 JavaScriptのシンボルはRubyのシンボルと同じですか?

いいえ、違います。

Rubyのシンボルは基本的に値を作成するためのリテラルです。同じシンボルを2回言及しても、同じ値が2回生成されます。

:foo == :foo

JavaScript関数Symbol()はシンボルのファクトリです。返される各値は一意です。

Symbol('foo') !== Symbol('foo')

7.9 よく知られたシンボルの表記:なぜSymbol.iteratorではなくSymbol.ITERATOR(など)ですか?

よく知られたシンボルは、名前が小文字で始まり、キャメルケースになっているプロパティに格納されます。ある意味、これらのプロパティは定数であり、定数にはすべて大文字の名前を使用するのが慣例です(Math.PIなど)。しかし、その表記法の理由は異なります。よく知られたシンボルは、通常のプロパティキーの代わりに使用されるため、「名前」は定数の規則ではなく、プロパティキーの規則に従います。

7.10 シンボルAPI

このセクションでは、ECMAScript 6のシンボルAPIの概要を示します。

7.10.1 関数Symbol

Symbol(description?) : symbol
新しいシンボルを作成します。オプションのパラメータdescriptionを使用すると、シンボルに説明を与えることができます。説明にアクセスする唯一の方法は、シンボルを文字列に変換することです(toString()またはString()を使用)。変換の結果は'Symbol('+description+')'です。

> const sym = Symbol('hello');
> String(sym)
'Symbol(hello)'

Symbolはコンストラクタとして使用できません。newを使用して呼び出すと、例外がスローされます。

7.10.2 シンボルのメソッド

シンボルが持つ唯一の有用なメソッドはtoString()です(Symbol.prototype.toString()を使用)。

7.10.3 シンボルを他の値に変換する

変換先 明示的変換 強制型変換(暗黙的変換)
boolean Boolean(sym) → OK !sym → OK
number Number(sym)TypeError sym*2TypeError
string String(sym) → OK ''+symTypeError
  sym.toString() → OK `${sym}`TypeError
object Object(sym) → OK Object.keys(sym) → OK

7.10.4 よく知られたシンボル

グローバルオブジェクトSymbolには、いわゆるよく知られたシンボルの定数として機能するいくつかのプロパティがあります。これらのシンボルを使用すると、プロパティキーとして使用することで、ES6がオブジェクトを処理する方法を構成できます。すべてのよく知られたシンボルのリストです。

7.10.5 グローバルシンボルレジストリ

すべての領域で同じシンボルが必要な場合は、次の2つのメソッドを使用してグローバルシンボルレジストリを使用する必要があります。

次へ:8. テンプレートリテラル