Symbolを使ったプロパティ定義とは?

Symbolは、JavaScriptで一意の値を作成するためのプリミティブ型です。Symbol()を使うと、他のプロパティ名と衝突しにくいユニークなプロパティキーを作成できます。
特に、ライブラリや外部コードと同じオブジェクトを扱う場合に、プロパティ名の衝突を避ける目的で利用されます。
ただし、Symbolは完全なプライベート機能ではありません。取得方法を知っていればアクセスできるため、現在のJavaScriptで本当にプライベートな値を扱いたい場合は、クラスのプライベートフィールド(#name)なども選択肢になります。
Symbolの基本的な使い方
Symbolを作成するには、Symbol()関数を使用します。引数には説明用の文字列を指定できますが、この文字列はあくまでデバッグ用の説明であり、同じ文字列を指定しても同じSymbolになるわけではありません。
Symbolの定義とプロパティの設定
const mySymbol = Symbol("uniqueKey");
const myObject = {
[mySymbol]: "このプロパティはSymbolキーを持ちます"
};
console.log(myObject[mySymbol]);
この例では、mySymbolをプロパティキーとして使用しています。
Symbolをプロパティキーにする場合は、オブジェクトリテラル内で[mySymbol]のように角括弧を使います。
Symbolのプロパティはユニーク
Symbolは、それぞれが一意の値です。同じ説明文字列を指定しても、作成されるSymbolは別物になります。
Symbolと通常のプロパティの違い
const obj = {
name: "Alice"
};
const sym1 = Symbol("key");
const sym2 = Symbol("key");
obj[sym1] = "値1";
obj[sym2] = "値2";
console.log(obj.name);
console.log(obj[sym1]);
console.log(obj[sym2]);
console.log(sym1 === sym2);
sym1とsym2は、どちらも"key"という説明を持っていますが、別々のSymbolです。
そのため、obj[sym1]とobj[sym2]は別のプロパティとして扱われます。
Symbolを使ったプロパティの特徴
Symbolを使ったプロパティには、通常の文字列キーのプロパティとは異なる特徴があります。
| 特徴 | 説明 |
|---|---|
| 一意性 | Symbol()で作成したSymbolは、それぞれ異なる値になります。 |
| 名前衝突の回避 | 他のコードが同じ名前のプロパティを追加しても衝突しにくくなります。 |
| 通常の列挙に出ない | for...inやObject.keys()では取得されません。 |
| 完全な非公開ではない | Object.getOwnPropertySymbols()を使うと取得できます。 |
| グローバル共有も可能 | Symbol.for()を使うと、同じキーでSymbolを共有できます。 |
Symbolを使用したプロパティの例
Symbolは、外部から簡単には見えない内部用プロパティを定義したい場合に使えます。
内部用プロパティの作成例
const PRIVATE = Symbol("privateProperty");
const myObj = {
[PRIVATE]: "これは内部用のプロパティです",
name: "Sample"
};
console.log(myObj[PRIVATE]);
for (const key in myObj) {
console.log(key);
}
console.log(Object.keys(myObj));
この例では、Symbolを使って内部用のプロパティを作成しています。
for...inやObject.keys()ではSymbolプロパティは表示されません。ただし、次のようにすればSymbolプロパティを取得できます。
const symbols = Object.getOwnPropertySymbols(myObj);
console.log(symbols);
console.log(myObj[symbols[0]]);
このように、Symbolプロパティは見つけにくいだけであり、完全に隠されているわけではありません。
グローバルシンボルの使用
Symbol.for()を使用すると、グローバルシンボルレジストリに登録されたSymbolを取得できます。
同じキーを指定すれば、別の場所からでも同じSymbolを取得できます。
Symbol.for()によるグローバルシンボルの共有例
const symbol1 = Symbol.for("sharedSymbol");
const symbol2 = Symbol.for("sharedSymbol");
console.log(symbol1 === symbol2);
const obj1 = {
[symbol1]: "オブジェクト1の値"
};
const obj2 = {
[symbol2]: "オブジェクト2の値"
};
console.log(obj1[symbol1]);
console.log(obj2[symbol2]);
この例では、symbol1とsymbol2は同じSymbolです。
Symbol()は毎回新しいSymbolを作りますが、Symbol.for()は同じキーに対して同じSymbolを返します。
Symbolのユースケース
Symbolは、次のような場面で利用されます。
- プロパティ名の衝突を避けたい場合。
- ライブラリ内部で使うプロパティを定義したい場合。
- 通常の列挙処理に出したくないプロパティを作りたい場合。
- JavaScriptの組み込み動作をカスタマイズしたい場合。
組み込みSymbolの例
JavaScriptには、特別な意味を持つ組み込みSymbolがあります。代表的なものにSymbol.iteratorがあります。
const numbers = [1, 2, 3];
const iterator = numbers[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
Symbol.iteratorは、オブジェクトを反復可能にする仕組みで使われます。
配列がfor...ofで処理できるのは、この仕組みを持っているためです。
Symbol.toStringTagの例
Symbol.toStringTagを使うと、Object.prototype.toString.call()で表示される名前をカスタマイズできます。
const customObject = {
[Symbol.toStringTag]: "CustomObject"
};
console.log(Object.prototype.toString.call(customObject));
この例では、オブジェクトの内部的な表示名としてCustomObjectを設定しています。
初心者が覚えておきたいポイント
Symbol()は一意の値を作成します。- 同じ説明文字列を指定しても、同じSymbolにはなりません。
- Symbolはオブジェクトのプロパティキーとして利用できます。
- Symbolプロパティは
Object.keys()やfor...inでは表示されません。 - Symbolは完全なプライベート機能ではありません。
- 同じSymbolを共有したい場合は
Symbol.for()を使います。
まとめ
- SymbolはJavaScriptのプリミティブ型の1つで、一意の値を作成できる。
- Symbolをプロパティキーにすると、名前の衝突を避けやすくなる。
Symbol()は呼び出すたびに異なるSymbolを返す。Symbol.for()を使うと、同じキーでSymbolを共有できる。- Symbolプロパティは通常の列挙処理には表示されない。
- Symbolは完全なプライベート機能ではないため、用途を理解して使うことが重要である。