演算子はよく利用する演算処理を記号などで表現したものです。これ以外にも演算子には多くの種類があります。演算子は演算する対象を持ちます。この演算子の対象のことを被演算子(オペランド)と呼びます。次のコードでは、+演算子が値同士を足し算する加算演算を行っています。このとき、+演算子の対象となっている1と2という2つの値がオペランドです。2;このコードでは+演算子に対して、前後に合計2つのオペランドがあります。このように、2つのオペランドを取る演算子を二項演算子と呼びます。オペランド2また、1つの演算子に対して1つのオペランドだけを取るものもあります。数値をインクリメントする++演算子は、次のように前後どちらか一方にオペランドを置きます。++num;このように、1つのオペランドを取る演算子を単項演算子と呼びます。単項演算子と二項演算子で同じ記号を使うことがあるため、呼び方を変えています。この章では、演算子ごとにそれぞれの処理について学んでいきます。演算子の中でも比較演算子は、JavaScriptでも特に挙動が理解しにくい暗黙的な型変換という問題と密接な関係があります。そのため、演算子をひととおり見た後に、暗黙的な型変換と明示的な型変換について学んでいきます。演算子の種類は多いため、すべての演算子の動作をここで覚える必要はありません。基本的な二項演算子を見ていきます。プラス演算子(+)2つの数値を加算する演算子です。データ型とリテラルを参照)。そのため、整数と浮動小数点数の加算もプラス演算子で行えます。(+)は、文字列を結合した文字列を返します。"文字列結合"つまり、プラス演算子(+)は数値同士と文字列同士の演算を行います。マイナス演算子(-)2つの数値を減算する演算子です。9.5乗算演算子(*)2つの数値を乗算する演算子です。5除算演算子(/)2つの数値を除算する演算子です。20剰余演算子(%)2つの数値のあまりを求める演算子です。べき乗演算子(**)2つの数値のべき乗を求める演算子です。左オペランドを右オペランドでべき乗した値を返します。16べき乗演算子と同じ動作をするMath.powメソッドがあります。16べき乗演算子はES2016で後から追加された演算子であるため、関数と演算子がそれぞれ存在しています。他の二項演算子は演算子が先に存在していたため、Mathには対応するメソッドがありません。単項演算子(算術)単項演算子は、1つのオペランドを受け取り処理する演算子です。単項プラス演算子(+)単項演算子の+はオペランドを数値に変換します。次のコードでは、数値の1を数値へ変換するため、結果は変わらず数値の1です。+数値のように数値に対して、単項プラス演算子をつけるケースはほぼ無いでしょう。1また、単項プラス演算子は、数値以外も数値へと変換します。次のコードでは、数字(文字列)を数値へ変換しています。1一方、数値に変換できない文字列などはNaNという特殊な値へと変換されます。NaNNaNは"Not-a-Number"の略称で、数値ではないがNumber型の値を表現しています。NaNはどの値とも(NaN自身に対しても)一致しない特性があり、Number.isNaNメソッドを使うことでNaNの判定を行えます。trueしかし、単項プラス演算子は文字列から数値への変換に使うべきではありません。Numberコンストラクタ関数やparseInt関数などの明示的な変換方法が存在するためです。詳しくは「暗黙的な型変換」の章で解説します。単項マイナス演算子(-)単項マイナス演算子はマイナスの数値を記述する場合に利用します。書くことができるのは、単項マイナス演算子を利用しているからです。-1また、単項マイナス演算子はマイナスの数値を反転できます。そのため、"マイナスのマイナスの数値"はプラスの数値となります。1単項マイナス演算子も文字列などを数値へ変換します。-1また、数値へ変換できない文字列などをオペランドに指定した場合は、NaNという特殊な値になります。そのため、単項プラス演算子と同じく、文字列から数値への変換に単項マイナス演算子を使うべきではありません。NaNインクリメント演算子(++)インクリメント演算子(++)は、オペランドの数値を+1する演算子です。オペランドの前後どちらかにインクリメント演算子を置くことで、オペランドに対して値を+1した値を返します。1;インクリメント演算子(++)は、オペランドの後ろに置くか前に置くかで、それぞれで評価の順番が異なります。後置インクリメント演算子(num++)は、次のような順で処理が行われます。numの評価結果を返すnumに対して+1するそのため、num++が返す値は+1する前の値となります。2一方、前置インクリメント演算子(++num)は、次のような順で処理が行われます。numに対して+1するnumの評価結果を返すそのため、++numが返す値は+1した後の値となります。2この2つの使い分けが必要となる場面は多くありません。そのため、評価の順番が異なることだけを覚えておけば問題ないと言えます。デクリメント演算子(--)デクリメント演算子(--)は、オペランドの数値を-1する演算子です。1;デクリメント演算子は、インクリメント演算子と同様に、オペランドの前後のどちらかに置くことができます。デクリメント演算子も、前後どちらに置くかで評価の順番が変わります。オペランド同士の値を比較し、真偽値を返す演算子です。厳密等価演算子(===)厳密等価演算子は、左右の2つのオペランドを比較します。同じ型で同じ値である場合に、trueを返します。オブジェクトの参照が同じである場合に、trueを返します。次のコードでは、空のオブジェクトリテラル({})同士を比較しています。オブジェクトリテラルは、新しいオブジェクトを作成します。そのため、異なるオブジェクトを参照する変数を===で比較するとfalseを返します。true厳密不等価演算子(!==)厳密不等価演算子は、左右の2つのオペランドを比較します。異なる型または異なる値である場合に、trueを返します。true===を反転した結果を返す演算子となります。等価演算子(==)等価演算子(==)は、2つのオペランドを比較します。同じデータ型のオペランドを比較する場合は、厳密等価演算子(===)と同じ結果になります。同じ型となるように暗黙的な型変換をしてから比較します。そのため、次のような、見た目からは結果を予測できない挙動が多く存在します。true意図しない挙動となることがあるため、暗黙的な型変換が行われる等価演算子(==)を使うべきではありません。代わりに、厳密等価演算子(===)を使い、異なる型を比較したい場合は明示的に型を合わせるべきです。例外的に、等価演算子(==)が使われるケースとして、nullとundefinedの比較があります。厳密等価演算子(===)では二度比較する必要があります。等価演算子(==)ではnullとundefinedの比較結果はtrueとなるため、一度の比較でよくなります。等価演算子(==)は暗黙的な型変換をするため、バグを引き起こしやすいです。そのため、仕組みを理解するまでは常に厳密等価演算子(===)を利用することを推奨します。不等価演算子(!=)不等価演算子(!=)は、2つのオペランドを比較し、等しくないならtrueを返します。false不等価演算子も、等価演算子(==)と同様に異なる型のオペランドを比較する際に、暗黙的な型変換をしてから比較します。falseそのため、不等価演算子(!=)は、利用するべきではありません。代わりに暗黙的な型変換をしない厳密不等価演算子(!==)を利用します。大なり演算子/より大きい(>)大なり演算子は、左オペランドが右オペランドより大きいならば、trueを返します。false大なりイコール演算子/以上(>=)大なりイコール演算子は、左オペランドが右オペランドより大きいまたは等しいならば、trueを返します。false小なり演算子/より小さい(<)小なり演算子は、左オペランドが右オペランドより小さいならば、trueを返します。false小なりイコール演算子/以下(<=)小なりイコール演算子は、左オペランドが右オペランドより小さいまたは等しいならば、trueを返します。falseビット演算子ビット演算子はオペランドを符号付き32bit整数に変換してから演算します。ビット演算子による演算結果は10進数の数値を返します。9という数値は符号付き32bit整数では次のように表現されます。"1001"また、-9という数値は、ビッグエンディアンの2の補数形式で表現されるため、次のようになります。"11111111111111111111111111110111"ビット論理積(&)論理積演算子(&)はビットごとのAND演算した結果を返します。0b1001ビット論理和(|)論理和演算子(|)はビットごとのOR演算した結果を返します。(^)はビットごとのXOR演算した結果を返します。0b0110ビット否定(~)単項演算子の否定演算子(~)はオペランドを反転した値を返します。これは1の補数として知られている値と同じものです。-0b10000否定演算子(~)はビット演算以外でも使われていることがあります。文字列(Stringオブジェクト)が持つindexOfメソッドは、マッチする文字列を見つけて、そのインデックス(位置)を返すメソッドです。このindexOfメソッドは、検索対象が見つからない場合には-1を返します。-1否定演算子(~)は1の補数を返すため、~(-1)は0となります。0JavaScriptでは0も、if文ではfalseとして扱われます。そのため、~indexOfの結果が0となるのは、その文字列が見つからなかった場合だけとなります。次のコードのような否定演算子(~)とindexOfメソッドを使ったイディオムが一部では使われていました。console.log("木を見つけました");}ES2015では、文字列(Stringオブジェクト)にincludesメソッドが実装されました。includesメソッドは指定した文字列が含まれているかを真偽値で返します。console.log("木を見つけました");}そのため、否定演算子(~)とindexOfメソッドを使ったイディオムは、includesメソッドに置き換えられます。左シフト演算子(<<)左シフト演算子は、数値であるnumをbitの数だけ左へシフトします。左にあふれたビットは破棄され、0のビットを右から詰めます。bit;次のコードでは、9を2ビット分だけ左へシフトしています。0b111100右シフト演算子(>>)右シフト演算子は、数値であるnumをbitの数だけ右へシフトします。右にあふれたビットは破棄され、左端のビットのコピーを左から詰めます。bit;次のコードでは、-9を2ビット分だけ右へシフトしています。左端のビットのコピーを使うため、常に符号は維持されます。0b11111111111111111111111111111101ゼロ埋め右シフト演算子(>>>)ゼロ埋め右シフト演算子は、数値であるnumをbitの数だけ右へシフトするのは右シフト演算子(>>)と同じです。異なる点としては右にあふれたビットは破棄され、0のビットを左から詰めます。次のコードでは、-9を2ビット分だけゼロ埋め右シフトしています。左端のビットは0となるため、常に正の値となります。0b00111111111111111111111111111101代入演算子(=)代入演算子(=)は変数に対して値を代入します。代入演算子については「変数と宣言」の章も参照してください。42また、代入演算子は二項演算子と組み合わせて利用できます。+=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、^=、|=のように、演算した結果を代入できます。assignment)今まで見てきた代入演算子は1つの変数に値を代入するものでした。分割代入を使うことで、配列やオブジェクトの値を複数の変数へ同時に代入できます。分割代入は短縮記法のひとつでES2015から導入された構文です。分割代入は、代入演算子(=)を使うのは同じですが、左辺のオペランドが配列リテラルやオブジェクトリテラルとなります。次のコードでは、右辺の配列の値を、左辺の配列リテラルの対応するインデックスに書かれた変数名へ代入します。2これは、次のように書いたのと同じ結果になります。array[1];同様にオブジェクトも分割代入に対応しています。オブジェクトの場合は、右辺のオブジェクトのプロパティ値を、左辺に対応するプロパティ名へ代入します。"value"これは、次のように書いたのと同じ結果になります。obj.key;条件(三項)演算子(?と:)条件演算子(?と:)は三項をとる演算子であるため、三項演算子とも呼ばれます。条件演算子は条件式を評価した結果がtrueならば、Trueのとき処理する式の評価結果を返します。条件式がfalseである場合は、Falseのとき処理する式の評価結果を返します。Falseのとき処理する式;if文との違いは、条件演算子は式として書くことができるため値を返します。"B"条件分岐による値を返せるため、条件によって変数の初期値が違う場合などに使われます。次の例では、text文字列にprefixとなる文字列を先頭につける関数を書いています。prefixの第二引数を省略したり文字列ではないものが指定された場合に、デフォルトのprefixを使います。第二引数が省略された場合には、prefixにundefinedが入ります。条件演算子の評価結果は値を返すので、constを使って宣言と同時に代入できます。"カスタム文字列"if文を使った場合は、宣言と代入を分ける必要があるため、constを使うことができません。基本的に真偽値を扱う演算子で、AND、OR、NOTを表現できます。AND演算子(&&)AND演算子(&&)は、左辺の値の評価結果がtrueであるならば、右辺の評価結果を返します。左辺の評価がtrueではない場合、右辺は評価されません。このような値が決まった時点でそれ以上評価しないことを短絡評価(ショートサーキット)と呼びます。falseAND演算子は、if文と組み合わせて利用することが多い演算子です。値が"str"である場合という条件をひとつの式として書くことができます。}}このときに、valueがString型でない場合は、その時点でfalseとなります。短絡評価はif文のネストに比べて短く書くことができます。AND演算子やOR演算子が3つ4つ連続する場合は複雑で読みにくいコードです。その場合は抽象化ができないかを検討するべきサインとなります。OR演算子(||)OR演算子(||)は、左辺の値の評価結果がfalseであるならば、右辺の評価結果を返します。AND演算子(&&)とは逆に、左辺がtrueである場合は、右辺を評価せずtrueを返します。trueOR演算子は、if文と組み合わせて利用することが多い演算子です。次のように、valueが0または1の場合にif文の中身が実行されます。console.log("valueは0または1です。");}NOT演算子(!)NOT演算子(!)は、オペランドの評価結果がtrueであるならば、falseを返します。falseNOT演算子は必ず真偽値を返すため、次のように2つNOT演算子を重ねて真偽値へ変換するという使い方も見かけます。falseこのようなケースの多くは、比較演算子を使うなどより明示的な方法で、真偽値を得ることができます。安易に!!による変換に頼るよりは別の方法を探してみるのがいいでしょう。falseグループ化演算子((と))グループ化演算子は複数の二項演算子が組み合わさった場合に、演算子の優先順位を明示できる演算子です。次のようにグループ化演算子で囲んだ部分が最初に処理されるため、結果も変化します。9演算子の優先順位はECMAScript仕様で定義されていますが、演算子の優先度をすべて覚えるのは難しいです。演算子の優先順位の中でグループ化演算子は優先される演算子となり、グループ化演算子を使って優先順位を明示できます。次のようなグループ化演算子を使わずに書いたコードを見てみましょう。xがtrueまたは、yかつzがtrueであるときに処理されます。true}ひとつの式に複数の種類の演算子が出てくると読みにくくなる傾向があります。このような場合にはグループ化演算子を使い、結合順を明示して書くようにしましょう。true}しかし、ひとつの式で多数の演算を行うよりも、式自体を分けたほうが読みやすい場合もあります。xとyが数値型の場合に処理するif文を考えてみます。グループ化演算子を使い、そのまま1つの条件式で書くことも可能ですが、読みにくくなってしまいます。`x`と`y`が数値型}このように無理して1つの式(1行)で書くよりも、条件式を分解してそれぞれの結果を変数として定義したほうが読みやすくなる場合もあります。それぞれの条件を分解して名前をつける(変数として定義する)ことも重要です。最後の式の評価結果を返します。次の例では、式1、式2、式3の順に評価され、式3の評価結果を返します。式3;これまでに、カンマで区切るという表現は、constによる変数宣言などでも出てきました。左から順に実行する点ではカンマ演算子の挙動は同じものですが、構文としては似て非なるものです。3一般にカンマ演算子を利用する機会はほとんどないため、「カンマで区切った式は左から順に評価される」ということだけを知っていれば問題ありません。1まとめこの章では演算子について学びました。callというものがあります。https://2ality.com/2014/01/eval.html。