【JavaScript】Null合体演算子と(??)とオプショナルチェイニング演算子(?.)

# はじめに

JavaScriptの言語使用は、Ecma Internationalによって定められており2015年からはECMAScriptとして毎年改定されています。

[ECMAScript2020](https://tc39.es/ecma262/)の仕様としてNull合体演算子(??)とオプショナルチェイニング演算子(?.)が追加されました。(その他にもいくつかの機能が追加されています。詳細は[こちらのリンク](https://ics.media/entry/200128/)にまとめられています。

Null合体演算子とオプショナルチェイニング演算子は、どちらも**nullableな変数に安全にアクセスする**という似たような目的を持っており、かつググラビリティが低いという特徴を持っています。

現時点(2020年)で最新の仕様であり、あまり現場で見かけることがないかもしれませんが、この２つの演算子の特徴を見ていきましょう。

## Null合体演算子

[Null合体演算子](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator)は、演算子の一種で左辺が`null`または`undefined`のときに右辺の値を返し、それ以外のときには左辺を返します。
C#、Swift、PHPなどにも採用されているため見覚えがある人も多いかもしれません。

## 構文

```js
A ?? B
```

```js
let text;

const title = text ?? "タイトル";
console.log(title); // タイトル
```

これは以下の構文と同意義で、シンタックスシュガーといえます。

```js
let text;

const title = text != null ? text : "タイトル";
console.log(title); // タイトル
```

## OR(||)演算子との違い

Null合体演算子導入前までは、同じような機能を提供するために、OR(||)演算子が使われていました。

```js
let text;

const title = text || "タイトル";
console.log(title); // タイトル
```

この結果を見ると、Null合体演算子とOR演算子どちらを使っても変わらないように思わます。

しかし、OR演算子は`null`や`undefined`に限らず**falsyな値0, '', NaN, null, undefined**のときに左辺の値を返すという特徴があります。

これは、例えば0をを正しい値として扱いたいときに思わぬ結果を返す可能性があります。

```js
const value = 0;

const a = value ?? "記録がありません。";
const b = value || "記録がありません。";

console.log(a); // 0
console.log(b); // 記録がありません。
```

## 短絡評価

Null合体演算子は、AND演算子やOR演算子のように短絡評価されます。
右辺の値が`null`または`undefined`でないことが証明されたとき、右辺の値は評価されません。

```js
const a = "a" ?? console.log("a");
const b = null ?? console.log("b"); // b
```

## AND演算子やOR演算子と同時に使えない

Null合体演算子を、AND演算子やOR演算子と直接同時に使おうとすると`syntax error`が発生します。

```js
const a = 0 || null ?? 'a'
const b = 1 && null ?? 'b'

// error: Uncaught SyntaxError: Unexpected token '??'
// const a = 0 || null ?? 'a'
```

Null合体演算子をAND演算子やOR演算子と使用したいときには、カッコをつけて明示的に優先順位をつける必要があります。

```js
const a = (0 || null) ?? "a";
const b = (1 && null) ?? "b";

console.log(a); // a
console.log(b); // b
```

## 使用例

Null合体演算子は、nullableな要素を使用する際に初期値を代入したいときに役に立ちます。

例えば、次の例はクエリパラメータに`username`が存在すればそれをページのタイトルとし、存在しないときには初期値を代入します。

```js
const params = (new URL(document.location)).searchParams;
document.title = params.get('username') ?? 'Default Title';
```

# オプショナルチェイニング演算子

[オプショナルチェイニング演算子(?.)](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Optional_chaining)は、ググりづらい上に名前まで長くて覚えづらいという厄介な演算子ですが(?)なかなか役にたちます。

オプショナルチェイニング演算子は、nullableなオブジェクトに対して安全にアクセスするための演算子です。これは、プロパティにアクセスするときに`.`の代わりに`?.`を使用して、オブジェクトが`null`または`undefiend`だった場合、実行時エラーを吐く代わりに`undefined`を返します。

## 構文

```js
obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)
```


```js
let user;

console.log(user.name);
// error: Uncaught SyntaxError: Unexpected token '?'
// console.log(user?.name)

console.log(user?.name);
// undefined

```

## オプショナルチェイニング演算子をネストした演算子に使う

オプショナルチェイニング演算子は、何個でも使用できるため次のようにネストしたプロパティに対しても使用することができます。

```js
const obj {
  first: {
    second: {
      third: 'hey!'
    }
  }
}

obj?.first?.secont?.third
```

## オプショナルチェイニング演算子を使用した関数呼び出し

オプショナルチェイニング演算子は安全に関数を呼び出すために使用することができます。
関数呼び出しには次のような構文に従います。

```js
obj.someFunction?.() 
```

しかし、この構文が有効に働くのは`someFunction()`が存在したいときだけです。以下の例のように、存在はするがメソッドではないプロパティに対して使用すると例外をスローします。

```js
const obj = {
  someFunction: "1",
};

obj.someFunction?.();
// error: Uncaught TypeError: obj.someFunction is not a function
// obj.someFunction?.();
```

## ブラケット表記法に使用する

```js
const obj = { 
   first: 1,
   second: 2
}

const page = 'first'

const content = obj?.[page]
```

## 配列の要素にアクセスするときに使用する

```js
const array = ["first", "second"];
const notArray = "1";

console.log(array?.[0]); // first
console.log(array?.[3]); // undefined
console.log(notArray?.[0]); // undefined
console.log(notArray?.[1]); // undefined
```

## 使用例

オプショナルチェイニング演算子は、APIから取得した要素や、DOMから取得した要素など、`null`や`undefined`で返されるオブジェクトにアクセスするときに役に立ちます。

例えば、次の例は`User型`のオブジェクトの配列からidをもとに特定のユーザーを見つけ出し名前を表示しようとしますが`find()`メソッドは`undefined`を返す可能性があります。

`if (user == null)`のように存在確認してからプロパティにアクセスすることもできますが、オプショナルチェイニング演算子を使えば簡潔に記述することができます。

```js
const users = [
  {
    id: 1,
    name: "Aron",
  },
  {
    id: 2,
    name: "Abel",
  },
  {
    id: 3,
    name: "Michael",
  },
];

const user = users.find((user) => user.id === 4);
console.log(user?.name);
```

# おわりに

~~[WikipediaのNull合体演算子のJavaScriptの項目](https://ja.wikipedia.org/wiki/Null%E5%90%88%E4%BD%93%E6%BC%94%E7%AE%97%E5%AD%90)誰か更新しといてください。~~

2021/06/17 更新されてました。