JS,TS系のGithubリポジトリを眺めていてSymbol()という記述を見かけました。 ライブラリのコアな部分で見かけたことはあるのですが、いまいち使い方が分かっていなかったので調べてみました。
まずは説明を読んでみましょう。
データ型 symbol は、プリミティブデータ型です。Symbol() 関数は、symbol 型の値を返します。これは組み込みオブジェクトを公開するための静的プロパティを持ち、グローバルシンボルレジストリを公開するための静的メソッドを持つので、組み込みオブジェクトクラスのようにも見えますが、コンストラクターとしての機能を持たず、"new Symbol()" はサポートされていません。
うーん、分かったようなそうでもないような。
こんな記述もあります。
Symbol() から返されるすべてのシンボル値は一意です。シンボル値は、オブジェクトプロパティの識別子として使用できます。
ポイントはこの記述で、要は任意のオブジェクトのプロパティを作れるということです。
const HOGE = Symbol('hoge');
const obj = {
[HOGE]: 'hogehoge'
};
上記のコードだけを見ると「別にhoge
というプロパティを作ればいいじゃん」と思うかもしれません。
// これと何が違う?
const obj = {
hoge: 'hogehoge'
};
例えば以下のようなレスポンスのAPIを想定します。 記事一覧を返してくれるようなAPIです。
const articleList = [
{no: 1001, title: "hoge"},
{no: 3004, title: "fuga"},
{no: 5005, title: "puni"}
]
no
はAPIの提供元で割り振られたものですが、自作アプリケーションのためにid
というプロパティを割り振ることを考えます。
// articleに任意のidの値を追加して返却する
const addId = (article) => {
return {
...article,
id : Math.floor( Math.random() * 100000 )
}
}
// APIから取得してきたデータにidを追加する
const processedArticleList = articleList.map(article => addId(article));
当面はこれで問題なく動作していました。
しかしながら後になってAPIの仕様が変更となり、API
のレスポンスで「今までno
だったプロパティ名がid
に変更になった」場合はどうでしょうか?
// API仕様変更に伴いnoがidになった
const articleList = [
{id: 1001, title: "hoge"},
{id: 3004, title: "fuga"},
{id: 5005, title: "puni"}
];
// articleに任意のidの値を追加して返却する
const addId = (article) => {
return {
...article,
id : Math.floor( Math.random() * 100000 )
}
}
// APIから取得してきたデータにidを追加する
const processedArticleList = articleList.map(article => addId(article));
この場合、元々レスポンスに含まれていたid
のプロパティをaddId
が上書きしてしまうことになります。
慌ててaddId
で付与するプロパティ名をcustomId
などに変えましたが、影響範囲が膨大になってしまったり・・・
こういったことをSymbol()
なら防ぐことができます。
Syombol()
は平たく説明すると「アプリケーション独自の重複しない定数プロパティ」です。
const id = Symbol('id');
// 共存できる
const obj = {
[id]: 'hogehoge',
id: 'fugafuga'
}
console.log(obj[id]); // --> hogehoge
console.log(obj.id); // --> fugafuga
こうすることで、先の例のように「意図しないプロパティの重複」を防ぐことができます。
細かい特徴として、以下のようにJSON
化した際にSymbol
を使ったプロパティの値は含まれない点があります。
console.log(JSON.stringify(obj)); // --> "{"id":"fugafuga"}"
このことから、Symbol
は基本的には「アプリケーション内で使用する、重複しないプロパティ」のような用途として使っていくことになります。
実は知らないうちにSymbol
を使っていることがあります。
Symbol
にはいくつか静的なプロパティが用意されています。
例えばSymbol.iterator
などです。
console.log(Symbol.iterator); // --> Symbol(Symbol.iterator)
これはイテラブルな値を使う際に使用できるプロパティで、みなさんご存知のfor-of
構文で内部的に使用されています。
イテラブルな値については、自分が過去に書いた以下の記事が参考になります。
参考:Zenn - TS】今さら聞けないイテレータ・ジェネレータ
今回はSymbol
について紹介しました。
アプリケーションのコアな部分や、自作のライブラリを作る場合などに用いることがほとんどなため、普段はあまり使用しないものかもしれません。
しかしながら、具体例で挙げたケースのように役立つ場面はあると思うので、一度手元でコードを動かしてみて「こんな感じなんだ」と触れておくといいのかと思います。
今回の内容が役立ちましたら幸いです。