Symbol

《 初回公開:2023/04/12 , 最終更新:未 》

【 目次 】

javascript symbol とは

JavaScript では、シンボルはプリミティブ値です。

Symbol データ型を持つ値は「シンボル値」として見ることができます。 JavaScript の実行時環境では、シンボル値は Symbol 関数を呼び出すことで生成され、動的に無名の一意の値を生み出します。シンボルはオブジェクトプロパティとして使用されることがあります。

プリミティブとリテラル,ラッパーオブジェクト,ボックス化(boxing)

ちょっとわき道にそれて、
プリミティブとは

JavaScript において、プリミティブ (primitive、プリミティブ値、プリミティブデータ型) はオブジェクトでなく、メソッドを持たないデータのことです。
すべてのプリミティブ値は、イミュータブル (immutable) 、つまり変更できません。

プリミティブ値がラッパーオブジェクトにより自動ボックス化されオブジェクトのようにふるまう。 例えば数値リテラルはNumberオブジェクトにボックス化されNumberオブジェクトのtoExponentialメソッドを利用する事ができる。

console.log((123).toExponential()); // 1.23e+2

わかっているようでよくわからない用語の使い方、プリミティブとリテラル。

数値リテラルによるプリミティブ値がラッパーオブジェクトにより自動ボックス化される。
言葉の使い方として、こういう表現でいいのかな。

Symbolオブジェクト

シンボルはラッパーオブジェクトにより自動ボックス化されSymbolオブジェクトに。

シンボルの生成

const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');

newを使ってインスタンスを生成できない。

let sym = new Symbol()  // TypeError

descriptionプロパティ

シンボル作成時に、引数を指定できる。
引数はdescriptionプロパティを参照できる。

console.log(symbol1.description)    // undefined
console.log(symbol2.description)    // 42
console.log(symbol3.description)    // foo

シンボルはユニーク

引数が同じでも異なる値になる。

let Sym1 = Symbol("Sym")
let Sym2 = Symbol("Sym")

console.log(Sym1 === Sym2) // "false" を返す

代入すれは同じ値を参照するので

Sym2 = Sym1;
console.log(Sym1 === Sym2) // "true" を返す

グローバルシンボル

通常のシンボルと異なり、
グローバルシンボルはグローバルシンボルレジストリーに登録されて。
グローバルシンボルが登録されていない場合は、新たにグローバルシンボルを登録してそれを返す。
登録済みの場合は同じ値を返す。

// 新たにグローバルシンボルfooを登録
let gSym1 = Symbol.for("foo");
// 既に登録済みのグローバルシンボルfooを取得
let gSym2 = Symbol.for("foo");

console.log(gSym1 === gSym2) // "true" を返す

Symbol.keyForメソッドを使って登録名を取得できるがそれはdescriptionプロパティと同じ値。

console.log(Symbol.keyFor(gSym1));  // foo
console.log(gSym1.description);     // foo

グローバルでないシンボルに対してkeyForメソッドを使っても意味がない。

let Sym1 = Symbol("Sym")
console.log(Symbol.keyFor(Sym1));   // undefined
console.log(Sym1.description);      // Sym

シンボルはプロパティのキーとして使用可能

シンボルはプロパティのキーとして使用するには[ ](ブラケット)で囲む必要がる。
そしてシンボルをキーとするプロパティと通常の文字列をキーとするプロパティへのアクセス方法の違いについて注意が必要。

let Sym = Symbol("Sym")

let obj={
    [Sym]: "シンボルをキーとするプロパティの値",
    Sym: "文字列Sym1をキーとするプロパティの値"
    // または "Sym": "文字列Sym1をキーとするプロパティの値"
}

console.log(obj.Sym)    // 文字列Sym1をキーとするプロパティの値
console.log(obj[Sym])   // シンボルをキーとするプロパティの値
console.log(obj["Sym"]) // // 文字列Sym1をキーとするプロパティの値

プロパティの記法にはドット表記とブラケット表記があるのだが、更にシンボルをキーに対する表記が加わって紛らわしい。

シンボルは何のためにあるの?何に使うの?

何に使うの?

シンボルの存在意義は、オブジェクトのプロパティのキーとしての利用。
シンボルの導入は、JavaScript互換性の維持の為。

ChatGPTに意見を聞いてみた。

Symbol は、ES6で導入された新しいプリミティブ型の一つで、一意の識別子を作成するために使用されます。JavaScriptのオブジェクトに対して、文字列や数値といった既存のプリミティブ型では表現できない一意の識別子を付与することができます。
Symbol を使用する場合、同じ値を持つ識別子を作成することはできません。これにより、オブジェクトのプロパティ名などに使用することで、プロパティ名の衝突を回避することができます。
以下は、Symbol を使用した例です。

const sym = Symbol(); // 新しいシンボルを作成する

const obj = {
  [sym]: "Hello" // オブジェクトのプロパティ名にシンボルを使用する
};

console.log(obj[sym]); // "Hello"
console.log(obj["Symbol()"]); // undefined
console.log(obj); // { [Symbol()]: 'Hello' }

このプロパティ名は一意の識別子であるため、同じ名前のプロパティが存在しないことが保証されています。
また、Symbol は、組み込みのシンボルとして、Symbol.iterator や Symbol.toPrimitive など、様々な用途で使用されています。これらのシンボルは、オブジェクトが持つ特定の機能を表現するために使用され、JavaScriptの標準仕様で定義されています。
Symbol は、プログラムの中で一意の識別子を作成する必要がある場合に使用されます。例えば、オブジェクトのプロパティ名を作成する場合や、イベントの種類を識別する場合などが挙げられます。

Symbol は、JavaScriptで一意の識別子を作成するために使われるデータ型の一つです。文字列や数値のような既存のデータ型とは異なり、Symbol はその値が重複しないことが保証されています。つまり、同じ Symbol を作成しても、それらは別々のものとして扱われます。
この一意性を利用して、プログラムの中で衝突を避けるための識別子を作成することができます。たとえば、オブジェクトのプロパティ名として Symbol を使えば、他のライブラリやコードとの衝突を回避しながら、自分自身の独自のプロパティを定義することができます。
また、組み込みの Symbol としては、例えば Symbol.iterator などがあります。これらは、オブジェクトがどのように反復処理されるかを定義するために使用されます。
Symbol を使うことで、プログラムの中で一意な値を扱うことができ、コードの品質を向上させることができます。ただし、Symbol は一度作成すると値を変更することができないため、用途をよく考えてから使う必要があります。

@@iterator

MDNのドキュメントを読んでいると、@@iteratorという意味の分からない演算子のような記号が出てきて何だろうと困惑してしまう。

調べてみると

ECMAScriptの仕様内でのwell-known symbols(よく知られているシンボル)の略記法のようで、あくまでも説明の為のもの。
@@iteratorとあったら実際のコードではこれを、Symbol.iteratorと記述する必要があるようだ。

Well-Known Symbolsにはどういうものがあるかと言えば

well-known symbolsというのは、システムで定義済みの組み込みシンボルという事かな。

例によって、ChatGPT先生に聞いてみると

@@iterator は、JavaScriptの内部メソッドの一つで、イテレーターを生成するために呼び出される特別なシンボルです。このシンボルは、JavaScriptのオブジェクトに対して呼び出すことができ、そのオブジェクトが反復処理可能な要素を持つ場合に、for...of ループや ... スプレッド構文といった構文で使用されます。

@@ という記号は、JavaScriptの内部的なプロトコルを指定するための命名規則で、オブジェクトによって使用される特別なシンボルの前につけられます。これにより、JavaScriptの仕様で定義されたシンボルと、ユーザー定義のシンボルを区別することができます。

例えば、配列オブジェクトには @@iterator メソッドがあり、これを呼び出すことで、配列内の要素を反復処理することができます。

ちなみに、Symbol.iteratorについてforやkeyForメソッドのふるまいを調べてみると。

console.log(Symbol.iterator.description);       // Symbol.iterator
console.log(Symbol.keyFor(Symbol.iterator));    // undefined

let gSym = Symbol.for("Symbol.iterator");
console.log(Symbol.iterator === gSym) // "false" を返す

Well-Known Symbolsであるシンボルはグローバルシンボルとはまた別の扱いになるらしい。

その他

シンボルプロパティはJSON.stringifyメソッドでは無視される。
for...inループやObject.keys()で列挙されない,無視される。

ページのトップへ戻る