もっと深くJavaScriptを知るための演算子の使い方

## はじめに
JavaScriptは演算子をうまく使うことで、値がnullのときの挙動を決めたり、デフォルト値を設定したりすることができます。

本記事では5つのTipsを紹介します。

## 前提知識
JavaScriptには型が存在していますが、明示的に型を宣言する必要はありません。
これは、実行時にJavaScriptが自動で型を評価してくれるためです。
また、型の変換も自動で行ってくれます。
（この仕様がバグの温床となることもあるのですが…）

中でも、真偽値に変換したとき`true`となる値を**truthy**な値、`false`となる値を**falsy**な値と呼びます。
以下はその一覧です。
| truthy | falsy |
|-|-|
| falsy以外のすべての値 | null、undefined、NaN、0、""(空文字)|

:::note warn
厳密には上記でfalsyをすべて網羅しているわけではありません。
細かく確認する場合は以下を確認してください。
https://developer.mozilla.org/ja/docs/Glossary/Falsy
:::

truthy、falsyの概念を抑えておいてください。

## "||"を使ったデフォルト値の設定
### 基本の説明
すでにご存知の方が多いかと思いますが、基本から確認します。

`||`は論理和と呼ばれる演算子です。
一般的には以下のように使います。

```javascript
if (score > 100 || score < 0) {
  // なにかの処理
}
```
設定した条件のうち、いずれかが真なら`true`、すべて偽なら`false`となる演算子であると認識しているかと思います。

しかし、その実態を正確に理解するのであれば
* 値がtruthyならばその値を返す
* falsyなら次の値を評価する

という挙動になっています。

どういうことか順を追って確認します。
```javascript
let score = 110;
if (score > 100 || score < 0) {
    // なにかの処理
}

score = -1;
if (score > 100 || score < 0) {
  // なにかの処理
}

score = 50;
if (score > 100 || score < 0) {
  // なにかの処理
}
```

#### scoreが110のとき
`score > 100`は真になるので、`score > 100`の評価結果である`true`が返ってきます。
よって、`if()`には`true`が設定されます。

#### scoreが-1のとき
`score > 100`は偽になるので、`score < 0`を評価します。
結果は真であるので、`if()`には`true`が設定されます。

#### scoreが50のとき
`score > 100`は偽になるので、`score < 0`を評価します。
こちらも偽になりますが、ほかに評価対象がないので`if()`には`false`が設定されます。

このように、一つずつ順番に評価して、truthyな値があればそれを返す。
なければ最後の値を返す、という挙動になっているのです。

### デフォルト値の設定
前置きが長くなりましたが、この挙動を応用すると値が未設定のときにデフォルト値を設定することができます。

`userName`という変数に対して、`user`オブジェクトの`name`プロパティを設定したいとします。
また`name`プロパティが未設定の場合、デフォルト値として`Taro`を設定したいです。

if文を使わずに、以下のように書くことができます。

```javascript
const userName = user.name || "Taro";
```

一見すると条件式でもないのに`||`を使っているのが奇妙に思えますが、先ほど確認した論理和の挙動をなぞれば簡単に理解できます。

#### user.nameがtruthy（設定済）の場合
`user.name`が設定されている場合、`true`であるとみなされます。
よって、`userName`には`user.name`が設定されます。
ここでのポイントはあくまでも`user.name`の値がそのまま設定されるということです。
（真偽値に変換後の値が設定されるわけではない）

#### user.nameがfalsy（未設定）の場合
`user.name`が`null`や`undefined`など未設定の場合、`false`とみなされます。
よって、`"Taro"`の評価に移ります。
`"Taro"`はtruthyなので`userName`には`"Taro"`が設定されます。

このように、falsyな値の場合には次の値に移ることを利用して、デフォルト値の設定をすることができました。

:::note warn
""（空文字）や、数値の0はfalsyであるため想定外の挙動になる可能性があり、考慮が必要です。
:::

## "&&"を使った条件付き処理実行
### 基本の説明
こちらも基本から確認します。

`&&`は論理積と呼ばれる演算子です。
一般的には以下のように使います。

```javascript
if (isAdmin && isAuthenticated ) {
  // なにかの処理
}
```

設定した条件がすべて真なら`true`、一つでも偽なら`false`となる演算子であると認識しているかと思います。

しかし、その実態を正確に理解するのであれば
* 値がfalsyならばその値を返す
* 値がtruthyなら次の値を評価する

という挙動になっており、`||`とは逆になっています。

つまり、一つずつ値を順番に評価して、falsyな値があればそれを返す。
なければ最後の値を返します。

### 条件付き処理の実行
ある条件を満たす場合にのみ、実行したい処理があるとします。
管理者の場合に、機能を有効化する処理を実行する場合を考えてみます。

if文を使わずに、以下のように書くことができます。

```javascript
isAdmin && enableFunction();
```

#### isAdminがtruthy（管理者）の場合
`isAdmin`は`true`なので、次の値の評価に移ります。
よって、`enableFunction`が実行されます。

#### isAdminがfalsy（管理者ではない）の場合
`isAdmin`は`false`なので、`false`がそのまま返ります。
評価はそこで終わるので、`enableFunction`の実行には到達しません。

このようにfalsyな値の場合にはそこで評価が止まることを利用して、条件を満たす場合にのみ実行される処理を定義することができました。

## "!!"を使った真偽値への変換
### 基本の説明
`!`は論理否定と呼ばれる演算子です。
一般的には以下のように使います。

```javascript
if (!isNaN(num)) {
  // 何らかの処理
}
```

真偽値を反転させます。`true`なら`false`に、`false`なら`true`にします。


### 真偽値への変換
`!`は真偽値でない値にも使用できます。
真偽値以外の場合には`truthy`なら`false`に、`falsy`なら`true`にします。

そして、`!!`は否定の否定、二重否定と呼ばれます。
つまり反対の反対なので、`truthy`なら`true`に、`falsy`なら`false`にします。

明示的に値を真偽値に変換したい場合に使用することができます。

```javascript
const userName = "Taro";
console.log(`${userName}:${!!userName}`); // Taro:true

const num = 100;
console.log(`${num}:${!!num}`); // 100:true

const hoge = undefined;
console.log(`${hoge}:${!!hoge}`); // undefined:false
```

ただ、これは`Boolean`関数を使っても同じことができるので、あまり使う機会はないかもしれません。

## オプショナルチェーン（?.）の使い方
今まで紹介した3つのtipsは従来のJavaScriptにもともとあった演算子を応用した使い方でした。

ここから紹介する2つのtipsは、比較的新しい書き方になります。

:::note info
ECMAScript（JavaScriptの仕様のようなもの）の2020バージョン（ES2020）から使える記法です。
:::

### 概要
オプショナルチェーンは、オブジェクトのプロパティが`null`または`undefined`のときに、エラーにせず`undefined`を返します。

### オプショナルチェーンを使わない場合
`users`オブジェクトの`fetchUserInfo`メソッドで、指定した`name`の情報から住所を取得したいとします。

```javascript
const userAddress = users.fetchUserInfo(name).address;
```

もし、`fetchUserInfo`の結果が`null`または`undefined`の場合、エラーが発生します。
`null`または`undefined`の値に対して`address`プロパティを取得しようとするためです。

値を取得できない可能性がある場合、if文を書いてチェックをする必要があります。

### オプショナルチェーンを使う場合
オプショナルチェーンを使うと、値のチェックが不要になります。

```javascript
const userAddress = users.fetchUserInfo(name)?.address;
```
`?.`を使っています。
`fetchUserInfo`の結果が`null`または`undefined`の場合、`address`の取得をせず、`undefined`を返却します。

これにより、エラーの発生を防ぐことができます。

## Null合体演算子（??）の使い方
こちらも新しい書き方です。

### 概要
`||`と似た挙動をします。

* 値がNull値（`null`、`undefined`）なら次の値を評価する
* 値がNull値でないならばその値を返す

という挙動になります。

`||`はtruthy、falsyでの判断だったため、""（空文字）や数値の0の場合には考慮が必要でした。
しかし、`??`はNull値の場合にのみ次の値に移るため、より正確に評価することができます。

### Null合体演算子を使わない場合
デフォルト値設定のため、`||`を使うとすると、意図しない挙動になる場合があります。

```javascript
// user.nameが""（空文字）の場合でも、デフォルト値が設定される
const userName = user.name || "Taro";
// user.hpが0の場合でも、デフォルト値が設定される
const hitPoint = user.hp || 50;
```
""（空文字）をそのまま設定したい、0をそのまま設定したい、という場合には`||`は使えません。

### Null合体演算子を使う場合
```javascript
// user.nameが""（空文字）の場合、デフォルト値が設定される
const userName = user.name ?? "Taro";
// user.hpが0の場合、デフォルト値が設定される
const hitPoint = user.hp ?? 50;
```
`??`を使うと、`null`または`undefined`の時のみデフォルト値が設定されるようになるため、""（空文字）や0をそのまま設定することが可能です。

ケースによって使い分けが必要ですが、基本的には`??`を使うのがよいでしょう。

## まとめ
演算子を利用して、便利に処理を書く方法を確認してきました。
うまく使えば、コード量を少なく、簡潔に書くことができます。
しかし、複雑な処理で多用すると、かえって可読性が下がる場合もあるので、状況に応じて使い分けることが重要です。