JavaScriptの論理演算子(&&, ||)を「かつ」「または」と覚えるのはもうやめよう

※こちらの記事は初学者向け・丁寧に解説を心がけております。
　その分、文章量も多いのでご了承ください。

プログラミングを学びたての頃、論理演算子の章で

- `&&`　を「**かつ**」
- `||`　を「**または**」

と覚えてしまった人も多いのではないでしょうか？
高校数学 A で習う「集合」のカリキュラムに登場する「`A∩B`」や「`A∪B`」の考え方に沿えば一番馴染みのある表現ではありますが、プログラムの振る舞いに着目すると厳密には異なります。

また、経験を積んでいくにつれて、`&&`を「**かつ**」、`||`を「**または**」と表現すると、説明がしづらいこと場面にも遭遇します。

この記事を読んで、「かつ」「または」という考え方から脱却していきましょう。

また、この記事では

- ブラウザですぐ実行できること
- 多くの初学者にも馴染みがある

という点からJavaScriptを基に解説していきます~~が、一部挙動は異なるものの、Ruby や PHP などでも考え方は同じです。~~

（追記：コメントをいただき調べましたところ、CやJava, PHPでは大きく挙動が異なるため、タイトルの先頭に**「JavaScriptの」**を追加させていただきました。誤解を生む表現を使ってしまい、申し訳ございませんでした。）

# 用語解説

## AND 演算子

`&&`のこと
アンパサンド２つと書くのがめんどくさい。

## OR 演算子

`||`のこと
パイプライン２つと書くのがめんどくさい。

## オペランド

> - 式を構成する要素だよ
> - 演算子じゃない方だよ
> - 数字とか変数とかだよ
>
> [オペランド (operand)とは｜「分かりそう」で「分からない」でも「分かった」気になれる IT 用語辞典](https://wa3.i-3-i.info/word13306.html)

`a || b`であれば、`a`や`b`のことです。
正式名称ではないかもしれませんが、この記事では`a`を**左オペランド**、`b`を**右オペランド**と言わせてください。

## Boolean

`true`と`false` の２つの値を取るデータ型のこと。

# 先に結論

- AND演算子`&&`は、左から順に`false`を探しにいき、
- OR演算子`||`は、左から順に`true`を探しにいき、
  - 見つかればその値を返す。
  - 見つからなければ最後の値を返すよ。
- `true/false`でない値は、
  - `true`に変換できる**truthy**な値か、
  - `false`に変換できる**falsy**な値かに着目しよう。

# よくある例題

初学者向け教材にはこんな例題が載っています。

> [例題１]
> 数学と理科のテストの点数を変数に格納し、
>
> - **どちらも 70 点以上**であれば「**合格**」
> - そうでなければ「**不合格**」
>
> と出力するプログラムを書いてみましょう。

```javascript:test1.js
const math = 80
const science = 65

if (math >= 70 && science >= 70) {
  console.log('合格！')
} else {
  console.log('不合格…')
}
```

頭の中で日本語に直すと

> if 文は
> ・もし (「math が 70 以上」**かつ**「science が 70 以上」）ならば
> という意味だな。
>
> math は 80 で 70 以上だから true だけど、science が 65 で 70 以上ではないので false。
> ２つとも true じゃないから、「かつ」を満たさない。
> よって、if 文全体の評価は false となり、else の方に進み、「不合格」が表示される。

こんな感じでしょうか。
この思考自体は間違っていません。この例題の回答としては十分クリアできています。

# 「かつ」「または」という考え方の弊害

しかし、AND 演算子`&&`を「**かつ**」と暗記していると、こんなイジワル問題に遭遇した時に、瞬時に答えられないかもしれません。

> [例題２]
> 以下のコードを実行すると、 console にどのように出力されるでしょうか？

```javascript:test2.js
const math = 80
const science = 65

if (math && science) {
  console.log('ifの中です')
} else {
  console.log('elseの中です')
}
```

変数を展開するとこうなりますが

```javascript
if (80 && 65)
```

`80かつ65`ってなんなんだ？と混乱してしまいます。

つまり、「かつ」「または」と覚えたことの弊害は、**左右のオペランドが論理式**（70 >= 80 のように、比較して true か false が返される式）**の場合に理解が限定されてしまうこと**です。

`>`や`==`などが出てこない場合でも、JavaScript がどのように判断しているのか理解できるようになりましょう。

# AND 演算子`&&`

AND 演算子`&&`は、[現代の JavaScript チュートリアル](https://ja.javascript.info/logical-operators#ref-302)ではこのように解説されています。

> ① 左から右にオペランドを評価します。
> ② それぞれのオペランドで、それを Boolean に変換します。
> ③ もしも結果が **false** の場合、ストップしそのオペランドの本来の値を返します。
> ④ もしもすべての他のオペランドが評価された場合(i.e. すべて **真** のとき), 最後のオペランドを返します。

順番に見ていきましょう。

**① 左から右にオペランドを評価する**

これはプログラミングの原則通りの向きなので問題ないでしょう。
「評価する」という表現がわかりにくければ、「１つ１つ見ていく」と変換しても構いません。

**② それぞれのオペランドで、それを Boolean に変換します。**

Boolean という言葉が分かりにくければ、「それぞれのオペランドで、それを真か偽のどちらかに変換する」と言い換えることができます。
では「Boolean に**変換する**」とはどういうことでしょうか？

## truthy / falsy

JavaScript には、

- `'リンゴ'`, `''(空文字)`などの**文字列**
- `0`, `-15`, `3.14`などの**数値**
- `['アメリカ', '日本', 'ドイツ']`などの**配列**
- `{ name: '鈴木', age: 20 }`などの**オブジェクト**

他にも `true`, `false`, `null`, `undefined` など、様々な型の値が存在しますが、それらは**全て true または false に変換することができます。**
変換した時に true になるものを**truthy**、false になるものを**falsy**と言います。

JavaScript では`false`そのものの他に、`0`, `''(空文字)`, `null`, `undefined`などは Boolean に変換すると`false`になることから**falsy**な値です。
falsy な値以外は全て Boolean に変換すると`true`になることから、**truthy**な値です。

もっと詳しく知りたい方は以下の記事をご覧ください。

- [Truthy (真値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN](https://developer.mozilla.org/ja/docs/Glossary/Truthy)
- [Falsy (偽値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN](https://developer.mozilla.org/ja/docs/Glossary/Falsy)

（ちなみに**Ruby**では**`nil`と`false`の２つだけが falsy**で、それ以外は全て**truthy**になります。
数値の `0` が JavaScript では falsy でも、Ruby では truthy になります。
ポートフォリオ作りで両方同時に学ぶときには注意しましょう。）

これを踏まえて、例題２の`80 && 65`をもう１度考えてみましょう。

- `80` は truthy な値なので `true` に、
- `65` も truthy な値なので `true` に変換されます。

```javascript
if (80 && 65)
↓
if (true && true)
```

**③ もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。**

`80` も `65` もどちらも `true` に変換されたため、`false` がありません。
③ はスキップします。

**④ もしもすべての他のオペランドが評価された場合(i.e. すべて 真 のとき), 最後のオペランドを返します。**

④ に該当するため、最後のオペランド、今回は `65` を返します。
つまり、`80 && 65`は評価されると`65`になるのです。

```javascript
if (80 && 65)
↓
-- if (true && true)を経由 --
↓
if (65)
```

論理演算子はなくなったものの、例題の答えにたどり着けていないので補足をすると、
if 文も AND 演算子のときと同様に、かっこの中の式を評価し、Boolean に変換した結果で、if の中に入るかどうかを決定します。

```javascript
if (65) {
  //if文がtrueと評価されるのでこちらに進みます。
  console.log("ifの中です");
} else {
  console.log("elseの中です");
}
```

先ほどと同様、`65`は**truthy**な値なので、この if 文全体は true となり、例題の答えは「`'ifの中です'`と表示される」でした。

## 例題１を振り返る

改めて一般的な論理式が含まれた if 文で復習してみましょう。

```javascript:test1.js
const math = 80
const science = 65

if (math >= 70 && science >= 70) {
  console.log('合格！')
} else {
  console.log('不合格…')
}
```

**① 左から右にオペランドを評価します。**
**② それぞれのオペランドで、それを Boolean に変換します。**

評価対象が元々論理式の場合は、Boolean に変換するというより、評価結果そのものが true か false なので、

- `math >= 70`の評価結果は`true`
- `science >= 70`の評価結果は`false`です。

と言うべきでしょうか。

```javascript
if (math >= 70 && science >= 70)
↓
if (true && false)
```

**③ もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。**

左から順に見ていって、左オペランドは`true`ですが、右オペランドは`false`なので右側でストップし、本来の値（今回は評価結果の`false`）が返されます。

```javascript
if (math >= 70 && science >= 70)
↓
-- if (true && false)を経由 --
↓
if (false)

--------------------------------

if (false) {
  console.log('ifの中です');
} else {
  //if文がfalseなのでこちらに進みます
  console.log('elseの中です');
}
```

いかがでしょうか？

当然「かつ」と読んでいたとしても結果は変わりませんが、この記事を読むまで AND 演算子`&&`を「かつ」と考えてきた人は、左右のどちらも`true`であることで式全体が「真」と評価されるように、`true`の方に重きを置かれがちですが、③ で見てきた通り、プログラムは`false`を探すことに一生懸命になっていますね。
着眼点が異なりますね。

この違いに慣れるまでは少し大変かもしれませんが、慣れてしまえば自然と読めてくるようになります。

# OR 演算子`||`

OR 演算子`||`は、AND 演算子`&&`の③④の太字部分を反転させたものです。

> ① 左から右にオペランドを評価します。
> ② それぞれのオペランドで、それを Boolean に変換します。
> ③ もしも結果が **true** であれば、停止しオペランドの本来の値を返します。
> ④ もしもすべての他のオペランドが評価された場合(i.e. すべて **偽** のとき), 最後のオペランドを返します。

例題を見ながら流れを確認していきましょう。

> [例題３]
> 以下のコードを実行すると、 console にどのように出力されるでしょうか？

```javascript:test3.js
if (0 || 0.1) {
  console.log('ifの中です');
} else {
  console.log('elseの中です');
}
```

AND 演算子のときほど丁寧には解説しませんが、

- `0` は falsy な値なので `false` に、
- `0.1`は truthy な値なので`true`に変換されます。
- 左から順に評価し、`true`を探しにいきます。
- 左オペランドは`false`ですが、右オペランドが`true`なので、ここで処理がストップし、本来の値`0.1`が返されます。

```javascript:test3.js
if (0 || 0.1)
↓
-- if (false || true)を経由 --
↓
if (0.1)
--------------------------------

if (0.1) {
  //if文がtrueと評価されるのでこちらに進みます。
  console.log('ifの中です');
} else {
  console.log('elseの中です');
}
```

OR 演算子`||`は、１つ１つ`true`を探すので、「または」のように「どちらかが」という文脈には沿っていますね。

# 役立つ場面

論理演算子を「かつ」「または」ではなく、プログラム本来の振る舞いで意識できると役立つ場面を紹介します。

## React の条件付きレンダー

React の JSX で使える書き方紹介の中に、AND 演算子`&&`が出てきます。

```react:sample.jsx
return (
  <div>
    <h1>Hello!</h1>
    {unreadMessages.length > 0 &&
      <h2>
        You have {unreadMessages.length} unread messages.
      </h2>
    }
  </div>
);
```

> これが動作するのは、JavaScript では true && expression は必ず expression と評価され、false && expression は必ず false と評価されるからです。
> [条件付きレンダー – React](https://ja.reactjs.org/docs/conditional-rendering.html#inline-if-with-logical--operator)

ここまで読んでくれた方であれば、上記解説の内容を既にご自分で理解できるようになっていると思いますが、

- AND 演算子`&&`は左から順に`false`を探しにいきます。
- 左オペランドが`true`なら右オペランドを見にいきます。
  - 右オペランドの HTML タグは truthy なので、最後の要素である右オペランドのHTMLタグが返される（＝表示される）
- 左オペランドが`false`ならそこで処理はストップし、`false`を返すため、右オペランドのHTMLタグは表示されない

という流れですね。

## OR 演算子を用いた代入

`a = b || c`のような OR 演算子`||`を用いた代入はよく見かけます。
例えば、メモアプリの投稿フォームで、

- タイトルを入力して投稿したらそのタイトルを
- タイトルを空欄のまま投稿したら現在日時に置き換えてデータベースに保存する

このような処理を考えてみましょう。

```javascript:memo.js
const defaultTitle = dateTimeFunction(); // '20220116_235940'などの現在日時を返す関数とする
const enteredTitle = formData.title; // ユーザーがメモ投稿フォームのタイトル欄に入力した内容

const title = enteredTitle || defaultTitle;
```

右オペランドが必ずtruthyになるように文字列を与えることで、

- 左オペランドの`enteredTitle`が何かしらの文字列なら、そこで処理はストップし、`enteredTitle`の中身を返す
- 左オペランドの`enteredTitle`が`''(空文字)`なら、右オペランドの`defaultTitle`の中身を返す

となります。

他にも、`a = a || b`のように、

- `a`がtruthyなら`a`
- `a`がfalsyなら`b`

を代入するものもあります。
（厳密には異なりますが、省略形の論理和代入`||=`なんてものもあったりしますが、滅多に出てきませんのであまり気にしないで大丈夫です。）

# まとめ

論理演算子の左右のオペランドは、どのような値でも`true`か`false`に評価できることから、必ずしも論理式である必要はありません。
①〜④ のルールを覚えておくことで、`&&`や`||`の左右に複雑なものが含まれていても、コードリーティング等で不用意に臆することがなくなるでしょう。
少々アカデミックな堅苦しい内容となりましたが、皆様のプログラミング学習のお役に立てばこの上なく嬉しく思います。

# 参考文献

- [オペランド (operand)とは | 「分かりそう」で「分からない」でも「分かった」気になれる IT 用語辞典](https://wa3.i-3-i.info/word13306.html)
- [論理演算子 | 現代の JavaScript チュートリアル](https://ja.javascript.info/logical-operators#ref-302)
- [論理積 (&&) - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Logical_AND)
- [論理和 (||) - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Logical_OR)
- [Truthy (真値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN](https://developer.mozilla.org/ja/docs/Glossary/Truthy)
- [Falsy (偽値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN](https://developer.mozilla.org/ja/docs/Glossary/Falsy)
- [条件付きレンダー – React](https://ja.reactjs.org/docs/conditional-rendering.html#inline-if-with-logical--operator)