[ ]
とラップされたキーを使用したプロパティへのアクセスSymbol.iterator
でSymbol.ITERATOR
ではないのか(など)?Symbol
記号はECMAScript 6の新しいプリミティブ型です。ファクトリ関数によって作成されます。
const
mySymbol
=
Symbol
(
'mySymbol'
);
ファクトリ関数を呼び出すたびに、新しく一意の記号が作成されます。オプションのパラメーターは、記号を出力表示するときに表示される記述的な文字列です(他の目的はありません)。
> mySymbol
Symbol(mySymbol)
記号は主に一意のプロパティキーとして使用されます。記号は他のプロパティキー(記号または文字列)と決して衝突しません。たとえば、Symbol.iterator
に格納されている記号をメソッドのキーとして使用することにより、オブジェクトを反復可能(for-of
ループやその他の言語メカニズムで使用可能)にすることができます(反復可能オブジェクトの詳細については、反復に関する章を参照してください)。
const
iterableObject
=
{
[
Symbol
.
iterator
]()
{
// (A)
···
}
}
for
(
const
x
of
iterableObject
)
{
console
.
log
(
x
);
}
// Output:
// hello
// world
行Aでは、記号がメソッドのキーとして使用されています。この一意のマーカーにより、オブジェクトが反復可能になり、for-of
ループを使用できるようになります。
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'
の場合とは異なります。
記号を文字列に強制変換(暗黙的に変換)すると、例外がスローされます。
const
sym
=
Symbol
(
'desc'
);
const
str1
=
''
+
sym
;
// TypeError
const
str2
=
`
${
sym
}
`
;
// TypeError
唯一の解決策は、明示的に変換することです。
const
str2
=
String
(
sym
);
// 'Symbol(desc)'
const
str3
=
sym
.
toString
();
// 'Symbol(desc)'
強制変換を禁止することで、いくつかのエラーを防ぐことができますが、記号の操作も複雑になります。
次の操作は、プロパティキーとしての記号を認識します。
Reflect.ownKeys()
[]
を使用したプロパティアクセスObject.assign()
次の操作は、プロパティキーとしての記号を無視します。
Object.keys()
Object.getOwnPropertyNames()
for-in
ループ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'
記号はプロパティキーとして使用できます。
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
プロパティのキーになることができる新しい種類の値が導入されたため、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
の方が、現在ではより良い選択です。
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()
のコードはまったく変化しません。これは、それらがいかに類似しているかを示しています。
他のキーと決して衝突しないキーを持つプロパティを作成できることは、次の2つの状況で役立ちます。
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()
を使用して、オブジェクトのすべての独自のキー(記号を含む)を見つけることができるからです。そこでも保護が必要な場合は、「クラスのプライベートデータ」でリストされているアプローチのいずれかを使用できます。
一意のアイデンティティを持つ記号は、「通常の」プロパティキーとは異なるレベルにある公開プロパティのキーとして理想的です。なぜなら、メタレベルキーと通常のキーは衝突してはならないからです。メタレベルプロパティの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
名前の衝突は重要ではないと思われる場合に備えて、JavaScript標準ライブラリの進化において名前の衝突が問題を引き起こした3つの例を次に示します。
Array.prototype.values()
が作成されたとき、with
が配列で使用され、外部スコープの変数values
をシャドウした既存のコードが壊れました(バグレポート1、バグレポート2)。そのため、with
からプロパティを非表示にするメカニズムが導入されました(Symbol.unscopables
)。String.prototype.contains
は、MooToolsによって追加されたメソッドと衝突し、String.prototype.includes
に名前変更する必要がありました(バグレポート)。Array.prototype.contains
もMooToolsによって追加されたメソッドと衝突し、Array.prototype.includes
に名前変更する必要がありました(バグレポート)。対照的に、プロパティキーSymbol.iterator
を使用してオブジェクトに反復可能性を追加することは、そのキーが他のものと競合しないため、問題を引き起こすことはありません。
次の表は、シンボルを明示的または暗黙的に他のプリミティブ型に変換した場合に何が起こるかを示しています。
変換先 | 明示的変換 | 強制型変換(暗黙的変換) |
---|---|---|
boolean |
Boolean(sym) → OK |
!sym → OK |
number |
Number(sym) → TypeError |
sym*2 → TypeError |
string |
String(sym) → OK |
''+sym → TypeError |
sym.toString() → OK |
`${sym}` → TypeError |
文字列への強制型変換が禁止されていることは、簡単に落とし穴になります。
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
強制型変換(暗黙的変換)は、シンボルでは多くの場合禁止されています。このセクションでは、その理由を説明します。
booleanへの強制型変換は常に許可されており、主にif
文やその他の場所で真偽値チェックを有効にするためです。
if
(
value
)
{
···
}
param
=
param
||
0
;
シンボルは特別なプロパティキーであるため、文字列(異なる種類のプロパティキー)への誤った変換を避ける必要があります。これは、加算演算子を使用してプロパティの名前を計算する場合に発生する可能性があります。
myObject
[
'__'
+
value
]
そのため、value
がシンボルの場合、TypeError
がスローされます。
シンボルを配列インデックスに誤って変換することも避けたいです。以下は、value
がシンボルの場合に発生する可能性のあるコードです。
myArray
[
1
+
value
]
そのため、この場合、加算演算子はエラーをスローします。
シンボルをbooleanに明示的に変換するには、Boolean()
を呼び出します。これは、シンボルに対してtrue
を返します。
> const sym = Symbol('hello');
> Boolean(sym)
true
Boolean()
は、内部操作ToBoolean()
を介して結果を計算します。これは、シンボルおよびその他の真偽値に対してtrue
を返します。
強制型変換もToBoolean()
を使用します。
> !sym
false
シンボルを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
シンボルを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
+
)による変換 加算演算子は次のように機能します。
ToString()
を介して)に強制変換し、それらを連結して結果を返します。文字列または数値への強制型変換は例外をスローするため、(直接)シンボルに対して加算演算子を使用することはできません。
> '' + Symbol()
TypeError: can't convert symbol to string
> 1 + Symbol()
TypeError: can't convert symbol to number
他のすべてのプリミティブ値にはリテラルがありますが、関数呼び出し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
[ ]
とラップされたキーによるプロパティへのアクセス 角括弧演算子[ ]
は通常、そのオペランドを文字列に変換します。現在、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'
プロパティを取得および設定するための演算子は、内部操作ToPropertyKey()
を使用します。これは次のように機能します。
String
を使用してToPrimitive()
を介してオペランドをプリミティブに変換します。[@@toPrimitive]()
がある場合、そのメソッドを使用してプリミティブ値に変換します。シンボルにはそのようなメソッドがあり、ラップされたシンボルを返します。toString()
を介してプリミティブに変換されます(プリミティブ値を返す場合)。それ以外の場合は、valueOf()
が使用されます(プリミティブ値を返す場合)。それ以外の場合は、TypeError
がスローされます。優先型String
は、最初にtoString()
が呼び出され、次にvalueOf()
が呼び出されることを決定します。ToString()
を介して結果を文字列に変換します。コードレルム(略してレルム)とは、コードの一部が存在するコンテキストです。これには、グローバル変数、ロードされたモジュールなどが含まれます。コードは正確に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
元の計画では、シンボルを使用してプライベートプロパティをサポートすることでした(公開シンボルとプライベートシンボルがありました)。しかし、プライベートデータの管理に「get」と「set」(2つのメタオブジェクトプロトコル操作)を使用することは、プロキシと適切に連携しません。
この2つの目標は相反しています。クラスに関する章では、プライベートデータの管理方法について説明しています。シンボルはそのオプションの1つですが、プライベートシンボルと同等の安全性を確保できません。これは、Object.getOwnPropertySymbols()
とReflect.ownKeys()
を使用して、オブジェクトのプロパティキーとして使用されているシンボルを特定できるためです。
シンボルは、ある意味ではプリミティブ値に似ており、またある意味ではオブジェクトに似ています。
それでは、シンボルとはプリミティブ値ですか、それともオブジェクトですか?最終的に、2つの理由からプリミティブ型になりました。
まず、シンボルはオブジェクトよりも文字列に似ています。言語の基本的な値であり、不変であり、プロパティキーとして使用できます。シンボルが一意のアイデンティティを持つことは、文字列に似ていることと必ずしも矛盾しません。UUIDアルゴリズムは、準一意の文字列を生成します。
第二に、シンボルはプロパティキーとして最も頻繁に使用されるため、JavaScriptの仕様と実装をそのユースケースに合わせて最適化することが理にかなっています。そうすれば、シンボルはオブジェクトの多くの機能を必要としません。
instanceof
、Object.keys()
などを使用して検査できます。シンボルがこれらの機能を持たないことで、仕様と実装が容易になります。V8チームも、プロパティキーに関して、プリミティブ型を特別なケースにする方が特定のオブジェクトにするよりも簡単であると述べています。
文字列とは対照的に、シンボルは一意であり、名前の衝突を防ぎます。これは、色などのトークンには便利ですが、Symbol.iterator
をキーとするもののようなメタレベルメソッドをサポートするには不可欠です。Pythonは、衝突を避けるために特別な名前__iter__
を使用します。プログラミング言語のメカニズムには、ダブルアンダースコアの名前を予約できますが、ライブラリはどうすればよいでしょうか?シンボルを使用すると、誰もが利用できる拡張メカニズムが得られます。後述のパブリックシンボルのセクションでわかるように、JavaScript自体ですでにこのメカニズムを十分に活用しています。
衝突のないプロパティキーに関して、シンボルに代わる仮説的な代替案が1つあります。命名規則を使用することです。たとえば、URLを含む文字列(例:'http://example.com/iterator'
)。しかし、これにより、プロパティキーの2番目のカテゴリが導入されます(通常は有効な識別子であり、コロン、スラッシュ、ドットなどを含まない「通常の」プロパティ名と対照的です)。これは基本的にシンボルが何であるかと同じです。そうなると、新しい種類の値を導入した方が良いでしょう。
いいえ、違います。
Rubyのシンボルは基本的に値を作成するためのリテラルです。同じシンボルを2回言及しても、同じ値が2回生成されます。
:foo
==
:foo
JavaScript関数Symbol()
はシンボルのファクトリです。返される各値は一意です。
Symbol
(
'foo'
)
!==
Symbol
(
'foo'
)
Symbol.iterator
ではなくSymbol.ITERATOR
(など)ですか? よく知られたシンボルは、名前が小文字で始まり、キャメルケースになっているプロパティに格納されます。ある意味、これらのプロパティは定数であり、定数にはすべて大文字の名前を使用するのが慣例です(Math.PI
など)。しかし、その表記法の理由は異なります。よく知られたシンボルは、通常のプロパティキーの代わりに使用されるため、「名前」は定数の規則ではなく、プロパティキーの規則に従います。
このセクションでは、ECMAScript 6のシンボルAPIの概要を示します。
Symbol
Symbol(description?) : symbol
新しいシンボルを作成します。オプションのパラメータdescription
を使用すると、シンボルに説明を与えることができます。説明にアクセスする唯一の方法は、シンボルを文字列に変換することです(toString()
またはString()
を使用)。変換の結果は'Symbol('+description+')'
です。
> const sym = Symbol('hello');
> String(sym)
'Symbol(hello)'
Symbol
はコンストラクタとして使用できません。new
を使用して呼び出すと、例外がスローされます。
シンボルが持つ唯一の有用なメソッドはtoString()
です(Symbol.prototype.toString()
を使用)。
変換先 | 明示的変換 | 強制型変換(暗黙的変換) |
---|---|---|
boolean |
Boolean(sym) → OK |
!sym → OK |
number |
Number(sym) → TypeError |
sym*2 → TypeError |
string |
String(sym) → OK |
''+sym → TypeError |
sym.toString() → OK |
`${sym}` → TypeError |
|
object |
Object(sym) → OK |
Object.keys(sym) → OK |
グローバルオブジェクトSymbol
には、いわゆるよく知られたシンボルの定数として機能するいくつかのプロパティがあります。これらのシンボルを使用すると、プロパティキーとして使用することで、ES6がオブジェクトを処理する方法を構成できます。すべてのよく知られたシンボルのリストです。
Symbol.hasInstance
(メソッド)C
がx instanceof C
の動作をカスタマイズできるようにします。Symbol.toPrimitive
(メソッド)Symbol.toStringTag
(文字列)Object.prototype.toString()
によって呼び出され、オブジェクトobj
のデフォルトの文字列の説明を計算します。
'[object '
+
obj
[
Symbol
.
toStringTag
]
+
']'
Symbol.unscopables
(オブジェクト)with
文から一部のプロパティを隠すことができます。Symbol.iterator
(メソッド)for-of
ループやスプレッド演算子(...
)などの言語構成によって反復処理できます)。このメソッドはイテレータを返します。詳細:「反復可能オブジェクトとイテレータ」の章を参照してください。String.prototype.match(x, ···)
はx[Symbol.match](···)
に転送されます。String.prototype.replace(x, ···)
はx[Symbol.replace](···)
に転送されます。String.prototype.search(x, ···)
はx[Symbol.search](···)
に転送されます。String.prototype.split(x, ···)
はx[Symbol.split](···)
に転送されます。詳細については、文字列に関する章の「正規表現の処理をパラメータに委譲する文字列メソッド」のセクションを参照してください。
Symbol.species
(メソッド)Array.prototype.map()
などの組み込みメソッドがthis
と同様のオブジェクトを作成する方法を構成します。詳細についてはクラスに関する章を参照してください。Symbol.isConcatSpreadable
(ブール値)Array.prototype.concat()
がオブジェクトのインデックス付き要素をその結果に追加するかどうか(「展開」)またはオブジェクトを単一要素として追加するかどうかを構成します(配列に関する章で詳細を説明)。すべての領域で同じシンボルが必要な場合は、次の2つのメソッドを使用してグローバルシンボルレジストリを使用する必要があります。
Symbol.for(str) : symbol
str
であるシンボルを返します。str
がまだレジストリにない場合は、新しいシンボルが作成され、キーstr
の下にレジストリに登録されます。Symbol.keyFor(sym) : string
sym
に関連付けられている文字列を返します。sym
がレジストリにない場合、このメソッドはundefined
を返します。このメソッドは、シンボルをシリアル化するために使用できます(例:JSON)。