a = b || c; はバッドノウハウ？

TL;DR

結局CoffeeScriptかよ！

## 君が思っているのと違う！

JavaScriptでは`a = b || c;`という書き方を見かけることがあります。その特殊形で`a = a || b;`というのも見かけます。ですが、これは本当にあなたが意図した書き方なのでしょうか？

`a = b || c;`という書き方について「もし`b`が存在するなら`a`に`b`を代入し、存在しないなら`c`を代入する」と解説される場合がありますが、**これは間違いです**。正しくは「もし`b`が真とみなされるのであれば`a`に`b`を代入し、偽とみなされるのであれば`c`を代入する」という意味です。ここで重要なのはJavaScriptにおいて何が真や偽とみなされるかです。

## 偽であるからと言って存在しないというわけでは無い。

JavaScriptのような動的型付け言語では全てのオブジェクト(ここではプリミティブ値を含む広義の意味です。以下、オブジェクトと表現した場合はこの意味を表します。[^1])について、条件分岐等の条件になったり、論理和・論理積の演算対象になったりすることができます。この場合、何をもって真であるか、偽であるかというのが重要です。JavaScriptでは次のものが偽とみなされます。

* `undefined`
* `null`
* `false`
* `+0`, `-0`, `NaN`
* `""`(空文字列)

上記以外は全て真とみなされます。ここで注目すべくは`undefined`、`null`、`false`以外も偽であると言うことです。

`undefined`は未定義である(未代入である)状態を表します。`null`はなんらかの意味のあるオブジェクトでは無い状態を表します。他のプログラミング言語ではこの二つを区別せずに一つのオブジェクトで表現することが多いのですが、JavaScriptでは歴史的な経緯で二つに分かれています。この二つは**非存在**を表現しており、偽であることは納得できるものです。

`false`は偽の代表であるため、偽であることは疑いようがありません。

では、`+0`、`-0`、`NaN`、`""`はどうなのでしょうか？他の数値や空では無い文字列に比べればそれは偽であっても良さそうなものです。それなら空のArray`[]`や空のObject`{}`も偽であっても良かったような気もします。なんとも中途半端ではありますが、無理矢理納得するとしましょう。

さてここで考えるのは「存在する」「存在しない」という判定において上で示す真・偽の基準で決めるのが妥当かどうかです。`false`が仕方が無いとしても、`+0`や`""`が存在しないとするにはいささか不自然です。色即是空・空即是色ではありませんが、零は存在するし、非数値も存在するし、空文字列もやはり存在します。

## 無視される存在。

では、何が問題なのでしょうか？次のコードを見てください。

```js:JavaScript
let a;
let b = 0;
let c = 1;
a = b || c
```

`a`は`1`になります。`b`が存在するにも関わらずです。`0`は全く意味の無い数値では無く、`0`には`0`という価値があります。それを無視してしまっているのです。

そう、問題は`0`が`""`が意味を持つときに、存在するかどうかと言う考えて使ってしまっていては問題が出ると言うことです。

```js:JavaScript
const div = (a , b) => {
  if (b != 0)
    return a / b;
  else
    return;
};
let s = 0;
let t = 2;
let x = div(s, t) || 42;
console.log(x);
```

`div`関数は0除算を考慮して第2引数が`0`の時は`undefined`を返すようになっています。`x`は`div`関数の結果が`undefined`である時を考慮して`undefined`だったときは`42`になるようにした**つもり**です。しかし、この演算の結果はもちろん`42`です。決して`0`ではありません。果たしてこれは意図通りの動作なのでしょうか？そんなことは無いでしょう。ぱっと見、きっと`0`になって欲しいと思っていたはずです。

## `a = b || c;`はバッドノウハウなのかも知れない。

PHPの`??`のように真のnull合体演算子であれば良かったのですが、`||`は(真偽値への変換をしない)ただの論理和です。ただの論理和であっても、Rubyが`nil`と`false`しか偽とみなさいように、せめて`null`と`undefined`と`false`だけが偽であればある程度有意義だったかも知れませんが、`0`や`""`も偽となってしまうためnull合体演算子として使うには無理があります。そう、JavaScriptには「もし`b`が存在するなら`a`に`b`を代入し、存在しないなら`c`を代入する」ということを簡潔に書く方法は存在しません。`a = b != null ? b : c;`のように三項演算子などを駆使して書く以外に方法は無いのです。

`a = b || c;`は本当にあなたの意図した通りの記述でしょうか？もし、「存在」というキーワードで考えているのであれば、それは誤りです。JavaScriptにおいて偽であることと存在しないことは同じではありません。あなたのコードにおいて零や空文字列が意味をなそうとしても、単純に`||`を使うとその意味がかき消されて、予期せぬ動作を引き起こすかも知れません。[^2]

## 【おまけと言う名の本編】CoffeeScriptの逆襲。

CoffeeScriptなら存在演算子(Existential Operator)`?`があります。

* `a ? b` … `a`が存在するなら`a`、存在しないなら`b`
* `a ?= b` … `a`が存在しないときのみ`b`を`a`に代入(`a = a ? b`とほぼ同じ[^3])
* `a?` … `a`が存在するなら真、存在しないなら偽
* `a?.b` … `a`が存在するなら`a.b`、存在しないなら`undefined`
* `f?(x)` … `f`が存在するなら`x`を引数にして`f`を呼出し、存在しないなら呼び出さずに`undefined`

上記の通り、`||`にはない様々な有意義な動きをします。ここでの「存在する」とは`undefined`でも`null`でも無い事を意味し、コンパイルでは`a != null`等の比較が使われます。

こんなに便利なのに誰も使わないのがCoffeeScriptの醍醐味です。

[^1]: ECMAScript仕様書ではオブジェクト(object)はオブジェクト型(Object Type)のもののみを指しますが、広い意味ではプログラミングにおいて操作等の対象となる何からのもののことを言います。端的に言うと変数が束縛されうるものです。オブジェクト指向はそのオブジェクトを中心に添えてプログラミングしていくパラダイムだから、オブジェクト指向と言っているのであり、オブジェクトという考えがあってこそオブジェクト指向が産まれたのであり、オブジェクト指向の用語としてオブジェクトという概念が産まれたのではありません。

[^2]: このことはPythonにも言えることです。Pythonにもnull合体演算子が無く、`0`や`""`が偽になります。

[^3]: `a ?= b`と言う表記では`a`が存在する場合代入自体が発生しないという利点がありますが、`a = a ? b`では必ず代入が発生します。