JavaScript で「?.」という書き方をする Optional chaining（オプショナルチェーン / オプショナルチェイニング）についての自分用メモ

この記事は、JavaScript のプログラムで利用可能な「?.」という書き方、Optional chaining（オプショナルチェーン / オプショナルチェイニング）」についての自分用のメモです。

名前を忘れた場合や仕様詳細を少し確認したい場合に、探そうとして検索をしようとしたら、地味に検索しづらい気がするので、自分がたどり着きやすいところにメモを作ろうと思ってこの記事を書きました。

## なぜ日本語の名称を 2つ併記しているか
タイトルを見て、「なぜ "オプショナルチェーン / オプショナルチェイニング" という併記をしているのだろう？」、と思われたかもしれないですが、それについて補足しておきます。
理由は単純で、JavaScript の情報を調べていてよくたどり着く「MDN」と「現代の JavaScript チュートリアル」で以下のように異なった記載がされていたためです。それで、念のため併記をしてみました。

●オプショナルチェーン (?.) - JavaScript | MDN
　https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Optional_chaining

●オプショナルチェイニング(Optional chaining) '?.'
　https://ja.javascript.info/optional-chaining

この後は記載をシンプルにするために、「オプショナルチェーン」のほうで統一して書き進めていこうと思います。

## オプショナルチェーンとは？
オプショナルチェーンは、「?.」の前の部分が undefined または null である可能性がある場合に、プログラムの記載を簡単化できるものです。

言葉だけでうまく説明できていない感じがするので、MDN に書かれた例に少し手を加えた実例を用いて、さらに続きを書いてみます。以下が、オプショナルチェーンを使ったプログラムの例です。

```javascript
const customer1 = {
  name: "yamada",
  details: {
    age: 30,
  },
};

const customer2 = {
  name: "suzuki",
};

console.log(customer1.details.age); // 出力は「30」
console.log(customer2.details?.age); // 出力は「undefined」
console.log(customer2.details.age); // エラー発生 「Uncaught TypeError: Cannot read properties of undefined (reading 'age')」
```

上記のプログラムの最後の行はエラーが発生します。原因は見て明らかかと思いますが、上記の「customer2」は customer1 と違って「details」が存在しないのに、 `customer2.details.age` を参照しようとしたためです。
※ エラーの内容は、上記のプログラムのコメントに書いた、「Uncaught TypeError: Cannot read properties of undefined (reading 'age')」という内容

一方、下から 2行目の `customer2.details?.age` を用いたほうだと、最終行との差は「?」が 1文字入っただけですが「エラーは発生せず、出力が undefined になる」という結果になります。
この部分の処理について、以下のように書けば `customer2.details?.age` を用いたのと同じ「undefined」という出力が得られますが、オプショナルチェーンを用いたほうが記載が簡単です。

```javascript
const output = customer2.details && customer2.details.age;
console.log(output);
```

ひとまず適当なサンプルで説明しましたが、「実際の利用例」もあったほうが良い気がしたので、自分がオプショナルチェーンを利用した例や、オプショナルチェーンが利用されている別の事例も書いてみます。

### 画像認識系のライブラリを使った時の事例
自分が利用した例の 1つに、手を認識対象として画像認識を行うライブラリを扱った時の話があります。

詳細は省略しますが、ライブラリで得られた結果を扱う部分で以下のような状況が想定されて、オプショナルチェーンを用いると処理をシンプルに書けそうだったため利用しました。

- 認識結果が「results」というオブジェクトで得られるが、そこで得られる中身は以下のパターンが想定される
   - 何らか認識を行う前段階で処理が行えていないときは、「results」は空
   - 認識処理は実行できたものの手を認識できない時は、要素ゼロの配列「results.marks」が得られる
   - 手を 1つ以上認識した時（※ 複数人の手を認識するのも可）は、「results.marks」の中身として、認識できた手の数に対応した複数の配列が得られる

上記の状況で、「認識処理は行えていて、1つ以上の認識結果が得られている場合」というのを判定するのに `if(results.marks?.length)` と書くと記載が短くできそうでした。

### ライブラリ「p5.toio」で使われた例
自分が最初に見た「オプショナルチェーンが使われた事例」でもある、「[p5.toio](https://tetunori.github.io/p5.toio/)」というライブラリでの事例です。

ライブラリに関する詳細な説明は省きますが、おおまかにライブラリの機能を説明すると「toio というデバイスへの Bluetooth 接続の処理や、toio に内蔵されているのモーターなどの Bluetooth経由での制御処理を行ったりする」というものです。
[このライブラリの Examples というページ](https://tetunori.github.io/p5.toio/docs/example)を見ていると、以下の画像のようにオプショナルチェーンが登場している箇所があります（他にも多数）。

![p5.toio で使われている事例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/d2fc8776-a647-a4f9-dae2-939e1c8660c1.png)

この事例では、Bluetooth を使ったデバイスとの接続が途中で途切れる可能性もある中で、その判定を含むデバイス制御の処理を簡単に書くために利用されていたようでした。

## オプショナルチェーンでできること（続き）
今回、あらためてオプショナルチェーンについて調べていたら、自分が把握していなかった使い方もあったため、それもメモとして記載してみます。

### 関数の呼び出しで使う
「現代の JavaScript チュートリアル」での説明で「[他のケース: ?.(), ?.\[\]](https://ja.javascript.info/optional-chaining#ref-2365)」という項目の中で説明されているもので、関数呼び出しで使う例です。
![関数の呼び出しで使う](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/7e0e9174-050d-1e38-096d-d689132c6ad9.png)

この場合は、括弧の前の部分に「?.」を記載する形になるようです。

### 括弧を使用したプロパティへのアクセスで使う
ドットではなく、括弧を使ってプロパティにアクセスする場合も、オプショナルチェーンが利用できるようです。

![括弧を使用したプロパティへのアクセスで使う](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/bb35417b-ae04-48af-be75-e5240a32c861.png)

### delete演算子と合わせて使う
[delete演算子](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/delete)との併用もできるようです。

```javascript
delete user?.name; // user が存在する場合に、 user.name を削除
```

## 構文まとめ
以下は、[MDN のオプショナルチェーンのページ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Optional_chaining) に記載された構文のまとめで、メモとして記載しておきます。

![構文まとめ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/af80f49b-9406-7e5f-3012-582adda16ad0.png)