【クイズでわかる】JavaScriptのSymbolとは?値が一致しない・ループに出てこない理由も解説

こんにちは!Reiです‎(˵ •̀ ᴗ – ˵) ✧


Symbolって、見かけることはあるけど、「結局どういうときに使うの?」と感じたことはないですか?

名前や見た目だけではイメージしづらくて、使い慣れてないとコードの中でどう扱われているのかがわかりづらいですよね… 💦


今回は、そんな Symbol の動きについてクイズ形式で整理していきます!

一つずつ確認して、「どういうルールで動いているのか」を確認しながら、Symbolをマスターしていきましょう ᥫᩣ ̖́-


第1問 Symbol() は同じ値になる?

まずはここから!

const s1 = Symbol("myID");
const s2 = Symbol("myID");

console.log(s1 === s2);

この結果はどうなるでしょうか?

  1. true
  2. false
  3. エラーになる

正解は… (ここをクリック!)

2の「false」です!


解説 !!

Symbol は、Symbol() と呼び出すと、オブジェクトのキーとして使える識別用の絶対に他と被らない値(Symbol型の値)を作成することができます。


Symbol("myID")

"myID" は、値そのものではなく「説明のためのラベル」のようなもの。

そのため、同じ "myID" を指定していても、Symbol() を呼び出すたびにまったく別の値が作られます。


なので、

const s1 = Symbol("myID");
const s2 = Symbol("myID");

この2つは見た目は同じですが、中身は別物が変数に格納されるので比較しても一致しないのです。


第2問 オブジェクトのキーにするとどうなる?

次はこちら!

const secretKey = Symbol("secret");

const user = {
  name: "Rei",
  [secretKey]: "12345"
};

for (const key in user) {
  console.log(key);
}

このコードを実行したとき、 ループで表示されるのはどれでしょうか?

  1. name と secretKey
  2. name だけ
  3. 何も表示されない

正解は… (ここをクリック!)

2の「name だけ」です!


解説 !!

Symbol の値をキーにしたプロパティは、通常のキーとは別の扱いになるためfor...in のループでは無視されるという特徴があります。


for (const key in user) {
console.log(key);
}

この for...in は、オブジェクトに設定された「文字列のキー」を順番に取り出すためのループです。

Symbol を使ったキーは文字列キーとは別の種類のキーとして扱われるため、for...in では取得対象にならないのです。


つまり、Symbol を使うことで、普段のループ処理では触れないようにしたい情報をオブジェクトに持たせることができるのです。


補足💡 [変数]: 値 の書き方について

[変数]: 値 は、変数の中身をキーとして使う書き方です。
数値や文字列なら文字列キー、SymbolならSymbolキーという扱いになります。


第3問 Symbol.iterator は何に使う?

次はこちら!

const myObj = {
  [Symbol.iterator]: function* () {
    yield 1;
    yield 2;
    yield 3;
  }
};

for (const value of myObj) {
  console.log(value);
}

このコードはどうなるでしょうか?

  1. 1, 2, 3 と順番に表示される
  2. [1, 2, 3] が表示される
  3. Symbol.iterator が見つからずエラーになる

正解は… (ここをクリック!)

1の「1, 2, 3 と順番に表示される」です!


解説 !!

for...of は、オブジェクトの中から値を順番に取り出すための書き方です。

ただし、どんなオブジェクトでもそのまま使えるわけではなく、「値をどう取り出すか」が定義されている必要があります。

そのときに使われるのが Symbol.iterator です。


Symbol.iterator は、JavaScript があらかじめ用意している特別な Symbol のひとつです。

このキーに関数を設定しておくことで、for...of は、オブジェクトの中にある Symbol.iterator を探し、そこに設定されている関数を使って値を取り出すことができます。


今回の

[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}

で使われている function* は、途中で止まりながら値を返せる関数です。

yield に到達すると、その時点で値を1つ返して処理が止まり、次に for...of が値を取りに来たときに、その続きから処理が再開されます!


そのため、

  • 1回目 → yield 1 → 1を返す
  • 2回目 → 続きから → yield 2 → 2を返す
  • 3回目 → 続きから → yield 3 → 3を返す

という順番で値を取り出すことができるのです 💡


第4問 Symbol.for() は何が違う?

最後の問題はこちら!

const globalS1 = Symbol.for("app.id");
const globalS2 = Symbol.for("app.id");

console.log(globalS1 === globalS2);

この結果はどうなるでしょうか?

  1. true
  2. false
  3. 毎回ランダムに変わる

正解は… (ここをクリック!)

1の「true」です!


解説 !!

Symbol() は呼び出すたびに新しい値を作りますが、Symbol.for() は少し動きが違うことに注意が必要です!


Symbol.for() は、JavaScript の中で共有されている「Symbol の一覧(レジストリ)」から、指定した名前に対応する Symbol の値を探して返します。

すでに同じ名前に対応する Symbol があればその値を返し、まだなければ新しい Symbol の値を作って、その一覧に登録します。


そのため、

const globalS1 = Symbol.for("app.id");
const globalS2 = Symbol.for("app.id");

のように、同じ "app.id" を指定すると、毎回同じ値が返されるのです。


ここまでのポイントおさらい !!

今回の内容をまとめると、次のとおり↓

Symbol の基本
Symbol() は呼び出すたびに新しい値(Symbol型)を作る
・同じ説明(”myID” など)でも別の値になる
・オブジェクトのキーとして使える識別用の値


Symbol をキーにしたときの特徴
・文字列キーとは別の種類として扱われる
for...in のループでは取得されない
・普段の処理から分離したい情報を持たせることができる


Symbol.iterator
for...of で値を取り出すためのルールを定義するために使われる
・このキーに関数を設定すると、その関数を使って値が取り出される
function*yield を使うことで、値を1つずつ順番に返せる


Symbol.for()
・指定した名前に対応する Symbol を共有して使う仕組み
・すでにあればそれを返し、なければ新しく作って登録する
・同じ名前を指定すると同じ値になる


Symbol は、オブジェクトのキーが他とぶつからないようにしたいときや、普段のループ処理では触れさせたくない情報を持たせたいときに使われます!

そういった場面に出会ったときに、「Symbol が使えたな」とふと思い出せると、他と干渉させずに扱いたいときに、Symbol を使うという選択ができるようになります。


最後に

Symbol は「知っている」だけだと出番が見えにくいですが、コードを見ているときに「なんでこれ Symbol なんだろう?」と一度立ち止まって考えてみると、その使い方にちゃんと理由があることに気づけます。

その理由を理解していく中で、背景にある意図や設計の考え方まで見えてくるはずです。

その積み重ねで、「ここは分けて持たせたいな」と考えたときに、Symbol という選択が自然と浮かぶようになるはず 💡

コメント

タイトルとURLをコピーしました