LoginSignup
25
10

More than 5 years have passed since last update.

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

Posted at

TL;DR

結局CoffeeScriptかよ!

君が思っているのと違う!

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

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

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

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

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

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

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

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

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

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

無視される存在。

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

JavaScript
let a;
let b = 0;
let c = 1;
a = b || c

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

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

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を返すようになっています。xdiv関数の結果がundefinedである時を考慮してundefinedだったときは42になるようにしたつもりです。しかし、この演算の結果はもちろん42です。決して0ではありません。果たしてこれは意図通りの動作なのでしょうか?そんなことは無いでしょう。ぱっと見、きっと0になって欲しいと思っていたはずです。

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

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

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

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

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

  • a ? baが存在するならa、存在しないならb
  • a ?= baが存在しないときのみbaに代入(a = a ? bとほぼ同じ3)
  • a?aが存在するなら真、存在しないなら偽
  • a?.baが存在するなら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では必ず代入が発生します。 

25
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
10