演算子は式が仕事をするための道具
JavaScriptで意味のあるコードを書くと、そのほぼすべての行には必ず演算子が登場します。演算子というのは、1つまたは2つの値を受け取って新しい値を生み出す記号のこと。+ は足し算、=== は比較、&& は真偽値の組み合わせ、? : は2つの値からどちらかを選ぶ、といった具合です。多くは他の言語と共通ですが、中にはJavaScript独特のクセを持つものもあるので、最初に押さえておくと後が楽になります。
ここではカテゴリごとに順番に見ていき、最後に使用頻度は低いけれど「ここぞ」という場面で役立つものを紹介します。
算術演算子
基本は想像どおりの動きです。
いくつか注意しておきたいポイントがあります。
/は常に浮動小数点の割り算になります。7 / 2は3ではなく3.5です。整数が欲しいときはMath.floor(7 / 2)やMath.trunc(7 / 2)を使いましょう。%は数学的な剰余(モジュロ)ではなく、余りを返します。符号は左オペランドに合わせられるので、-7 % 3は2ではなく-1になります。+はオーバーロードされています。どちらか一方が文字列だと、加算ではなく文字列連結になります。たとえば"3" + 1は"31"。これについては後ほど詳しく触れます。
インクリメントとデクリメント
count++ と ++count の違いは、同じ行でその式の値を使うときだけ関係してきます。単独の文として書くなら、どちらも同じ結果になります。多くのスタイルガイドでは、わかりやすさの観点から count += 1 を推奨しています。
+ 演算子には2つの顔がある
これは誰もが一度はハマるポイントです:
オペランドのどちらか一方が文字列だと、+ は文字列連結として動作し、もう片方も文字列に変換されます。一方、それ以外の算術演算子は真逆の挙動で、文字列の方を数値に変換します。
要点はシンプルです。文字列を組み立てたいならテンプレートリテラル(`price: ${5}`)を使う。計算をしたいなら、入力が本当に数値かどうかをきちんと確かめる。明示的に変換したいときは Number(x) や parseInt(x, 10) を使えばOKです。
JavaScript 比較演算子
比較演算子は真偽値(boolean)を返します。大きく分けて「厳密比較」と「緩い比較」の2種類があります。
=== と !== は 厳密等価 で、値と型の両方が一致しているかを見ます。一方 == と != は比較する前に型変換(型強制)が走るので、null == undefined が true になったり、[] == false が true になったりと、ハマりどころが多いです。基本は === と !== を使いましょう。よく使う例外は x == null くらいで、これは「x が null または undefined のどちらかか?」をサクッと判定できる便利なイディオムです。
大小比較の演算子は、数値なら期待どおりに動き、文字列なら辞書順で比較されます。
文字列の比較は文字コードをもとに行われるため、大文字と小文字が区別されます。人間にとって自然な並び順にしたい場合は、String.prototype.localeCompare を使いましょう。
論理演算子
JavaScript の論理演算子には &&(AND)、||(OR)、!(NOT)の3つがあり、真偽値を組み合わせるために使います。とはいえ、単なるブール代数に収まらない面白い挙動を持っています。
ここがポイントなのですが、&& と || は true や false を返すわけではなく、オペランドのどちらかを返します。&& は最初の falsy な値を返し、すべてが truthy ならいちばん最後の値を返します。|| は最初の truthy な値を返し、すべてが falsy なら最後の値を返します。
だからこそ const displayName = user.name || "Guest" のような書き方をよく見かけます。空っぽじゃない最初の値を拾うというパターンですね。短くて済むのは便利ですが、注意点が1つ。|| は 0、""、false もフォールバック対象として扱ってしまいます。これらを正当な値として扱いたいなら、代わりに ??(後述)を使いましょう。
ちなみに、どちらの演算子も 短絡評価(ショートサーキット) されます。つまり、左辺だけで結果が確定する場合、右辺は一切実行されません。
Null合体演算子 ??
?? は || と似た挙動をする演算子ですが、フォールバックが発動するのは値が null または undefined のときだけです。0 や ""、false では発動しません。
?? が向いているのは、0 や空文字列、false のように本来は意味のある値がフォールシーになり得るケースです。一方で「フォールシーな値はどれも『未指定』とみなしたい」なら || を使えばOK。最近のコードでは、null/undefined のときだけデフォルトを当てたい場面が多いので、?? を第一候補にしておくのが安全です。
代入演算子(Assignment Operators)
= は値を代入する演算子です。これに他の演算子を組み合わせた「複合代入演算子」も用意されています。
論理代入演算子というものもあって、||=、&&=、??= の3つです。現在の値が特定の条件を満たしたときだけ代入を行います。
これらは、冗長な if 文を書かずにデフォルト値を埋めたいときに便利です。
三項演算子の使い方
condition ? a : b は if/else を式にしたものです。条件が truthy なら a を、そうでなければ b を返します。
三項演算子は、短い値の選び分けにこそ真価を発揮します。ただしネストし始めた瞬間に読みづらくなるので、a ? b : c ? d : e のようなコードを書きそうになったら、素直に if/else やルックアップ用のオブジェクトに切り替えましょう。
typeof と instanceof
typeof は、オペランドの型を表す文字列を返します。
覚えておきたい落とし穴が2つあります。まず typeof null は "object" を返します(これは1995年から続くバグで、もはや修正不可能な仕様になっています)。そして配列も "object" として扱われます。配列かどうかは Array.isArray(x)、null かどうかは x === null でチェックしましょう。
instanceof は、あるオブジェクトが特定のコンストラクタから生成されたかどうかを判定します。
スプレッドとレストは同じ ... を使う
... は2つの場面で登場します。まずは スプレッド構文 で、これはイテラブルを個々の要素に展開します。
レスト として使うと、複数の値を1つの配列にまとめることができます。関数のパラメータや分割代入でよく使われます:
構文は同じでも、役割は真逆です。スプレッドは「展開」、レストは「集約」。どちらとして動くかは文脈で決まります。関数呼び出しやリテラルの中なら展開、パラメータリストや分割代入のパターンの中なら集約です。
演算子の優先順位(迷ったらカッコで囲もう)
演算子には優先順位があり、複数を組み合わせたときにどれが先に評価されるかが決まります。掛け算は足し算より優先、比較演算子は論理演算子より優先、といった具合です。
演算子の優先順位の表はかなり長くて、全部暗記している人なんてまずいません。とはいえ、99%のケースをカバーする簡単なコツがあります。それは、演算子を組み合わせるときに順序に自信がなければ、迷わずカッコを付けること。こうしておけばコードが読みやすくなりますし、読む人の記憶力に頼らずに済みます。
ビット演算子(使う場面はほぼない)
一応触れておくと、&、|、^、~、<<、>>、>>> といった演算子は、整数の2進数表現に対して動作します。グラフィック関連のコードや低レベルのプロトコル、あるいはビットマスクでフラグを扱うAPIなどで目にすることがあります。
よくある怪しいテクニックとして、n | 0 で数値を32ビット整数に切り詰めるというものがあります。昔は Math.trunc より速いと重宝されていましたが、今は使わないほうが無難です。Math.trunc のほうが意図が明確ですし、32ビットの範囲を超える数値でもちゃんと動きます。
次は if/else
演算子は値を生み出すもので、if/else はその値を使ってどの分岐を実行するかを決めます。ここまで見てきた比較演算子や論理演算子の結果は、ほとんどの場合こうした条件分岐に渡すことになります。次のページではまさにそれを扱います。
よくある質問
JavaScriptの主な演算子にはどんなものがありますか?
大きく分けると、算術演算子(+、-、*、/、%、**)、比較演算子(===、!==、<、>)、論理演算子(&&、||、!)、代入演算子(=、+=、-=)があります。さらに三項演算子 ? : や typeof、Null合体演算子 ?? といった特殊なものも。式や制御フローを組み立てる基本パーツなので、ひと通り押さえておきたいところです。
== と === の違いは?
=== は値と型の両方を厳密にチェックします。一方 == は型を変換してから比較するので、たとえば 0 == "0" は true ですが 0 === "0" は false になります。基本は === を使うのがおすすめ。== の型変換ルールはかなりクセがあって、コードレビューでも見落としやすいバグの温床になりがちです。
三項演算子って何に使うの?
condition ? a : b は、if/else を1行で書いて値を返せる書き方です。condition が truthy なら a、そうでなければ b が返ります。const label = count === 1 ? 'item' : 'items' みたいな短い条件分岐にはピッタリですが、三項演算子をネストし始めると一気に読みにくくなるので要注意です。
|| ではなく ?? を使うべき場面は?
|| ではなく ?? を使うべき場面は?|| は 0、""、false も含めて falsy な値すべてでフォールバックが働きます。一方 ?? は null と undefined のときだけ。たとえば count ?? 10 で 0 をちゃんと 0 として扱いたいなら ?? 一択です。逆に「falsy なら全部フォールバックでOK」というケースなら、これまで通り || で問題ありません。