やたらと ? が多いプログラム

JavaScript のコードで ? を見かけることは多いですが、実は ? を使う演算子は複数あり、それぞれ異なる役割を持っています。

突然ですが、皆さんは以下の JavaScript の実行結果が何になるかわかりますか?

const a = { b: "c" };
const b = { c: "d" };
console.log(a?.b?.c ?? b?.c ?? "?");

このプログラムには Javascript の ? を使う演算子が 2 種類含まれています。
ひとつずつ解説したうえで、記事の最後に上記プログラムの答えを記述します。

オプショナルチェーン ( ?. )

JavaScript では null や undefined のプロパティにアクセスすると、例外が発生してプログラムが停止してしまいます。
そのため、null の可能性がある値にアクセスする場合は、通常以下のように null チェックを行います。

if (cat !== null) console.log(cat.name);

これをオプショナルチェーンで記述すると下記のように短く書くことができます。

console.log(cat?.name);

cat が null や undefined だった場合でも例外は発生せず、コンソールには undefined と出力されます。

null 合体演算子 ( ?? )

JavaScript で、変数の値が null でなければその変数を参照し、null の場合は別の値を使いたいという場面はよくあります。
この処理を三項演算子で記述すると以下のようになります。

console.log(cat != null ? cat : "猫");

これを null 合体演算子を用いて記述することで下記のようによりスマートに記述することができます。

console.log(cat ?? "猫");

null 合体演算子は左辺が null または undefined の時に、右辺を返します。
値が無い場合のデフォルト値を設定するときによく利用します。

まとめ

これらを踏まえて最初のコードをもう一度見てみると何が起こるのかわかると思います。

const a = { b: "c" };
const b = { c: "d" };
console.log(a?.b?.c ?? b?.c ?? "?");

まず、一番左 ( a?.b?.c ) について a は b を持っていますが、b は c を持たないため、オプショナルチェーンによって undefined が返されます。
真ん中 ( a?.b?.c ?? b?.c ) は、左辺が undefined なので b?.c が返されます。
b は c を持っているので、結果 b.c に格納されている "d" が出力されます。
一番右 ( ?? "?" ) は b?.c が undefined ではないため評価されません。

上記のようにオプショナルチェーンと null 合体演算子は合わせてよく使うので、覚えておくと便利です。