JavaScriptにおいて、演算子はよく利用する演算処理を記号などで表現したものです。算術式や比較式、論理式、代入式などで使われます。たとえば、足し算をする +
も演算子の一種です。これ以外にも演算子には多くの種類があります。
ほとんどの演算子は +
や =
などの記号で表されます。しかし、delete
や instanceof
のようにキーワードで表される演算子もあります。キーワードで表わされる演算子も、記号で表される演算子と同じく普通の演算子です。単に読みやすい表記になっているだけです。
下表は、演算子の優先順位順にまとめています。初めの方に記述している演算子のほうが、後ろの方の演算子よりも優先順位が高くなります。表中の横線で、演算子の優先順位が変わります。A(associativity)は式の結合性を示します。L は左から右の順( left-to-right )で評価する、R は右から左の順( right-to-left )で評価する、という意味です。N という列はオペランドの数を表します。型という名前の列には、想定するオペランドの型と、→の後に、演算子の結果の型を列挙しています。
演算子 | 説明 | A | N | 型 |
---|---|---|---|---|
++ -- - + ~ ! delete typeof void |
前置または後置のインクリメント 前置または後置のデクリメント 数値の符号反転 数値に変換 ビット単位補数 論理補数 プロパティの削除 オペランドのデータ型を返す 未定義値を返す |
R R R R R R R R R |
1 1 1 1 1 1 1 1 1 |
左辺値→数値 左辺値→数値 数値→数値 数値→数値 整数→整数 論理値→論理値 左辺値→論理値 任意→文字列 任意→未定義値 |
*、/、% | 乗算、除算、剰余 | L | 2 | 数値、数値→数値 |
+、- + |
加算、減産 文字列の連結 |
L L |
2 2 |
数値、数値→数値 文字列、文字列→文字列 |
<< >> >>> |
左シフト 右シフト(左端に符号ビットをコピー) 右シフト(左端ビットは 0 ) |
L L L |
2 2 2 |
整数、整数→整数 整数、整数→整数 整数、整数→整数 |
<、<=、>、>= <、<=、>、>= instanceof in |
数値順で比較 アルファベット順で比較 オブジェクトのクラスを調べる プロパティが存在するかを調べる |
L L L L |
2 2 2 2 |
数値、数値→論理値 文字列、文字列→論理値 オブジェクト、関数→論理値 文字列、オブジェクト→論理値 |
== != === !== |
値が等しいかどうかをテストする 値が等しくないかどうかテストする 値が同じであるかどうかをテストする 値が同じでないかどうかをテストする |
L L L L |
2 2 2 2 |
任意、任意→論理値 任意、任意→論理値 任意、任意→論理値 任意、任意→論理値 |
& | ビット単位 AND | L | 2 | 整数、整数→整数 |
^ | ビット単位 XOR | L | 2 | 整数、整数→整数 |
| | ビット単位 OR | L | 2 | 整数、整数→整数 |
&& | 論理 AND | L | 2 | 任意、任意→任意 |
|| | 論理 OR | L | 2 | 任意、任意→任意 |
?: | 条件演算子 | R | 3 | 論理値、任意、任意→任意 |
= *=、/=、%=、+=、-=、&=、 ^=、|=、<<=、>>=、>>>= |
代入 演算を伴う代入 |
R R |
2 2 |
左辺値、任意→任意 左辺値、任意→任意 |
, | 最初のオペランドを無視し、 2番目のオペランドを返す |
L | 2 | 任意、任意→任意 |
演算子は演算するための対象を持ちます。この演算子の対象のことを被演算子(オペランド)と呼びます。演算子は、このオペランドの数で3種類に大別できます。
1つは二項演算子です。すでに出てきましたが、乗算演算子( *
)などがそうです。2つの式を組み合わせて、より複雑な 1つの式を作ります。操作対象となるオペランド(被演算子)が 2つ必要なので、二項演算子と呼ばれます。
2つ目は単項演算子です。1つの式をもっと複雑な別の式に変換します。例えば、-x
という式の -
がそうです。この演算子は、オペランド x
に対して符号反転を実行します。
最後の 1つは三項演算子です。JavaScript で使用できる三項演算子は、条件演算子( ?:
)だけです。?:
は、3つの式を組み合わせて 1つの式を作る演算子です。
任意の型の値を処理できる演算子もありますが、多くの演算子はオペランドとして特定の型を必要としています。また、同じように多くの演算子は、ある特定の型の値を返します。上表の「型」列にオペランドの型と、演算子の演算結果の型を示しています。矢印の前がオペランドの型で、矢印の後が演算結果の型です。
JavaScript の演算子は、必要に応じてオペランドの型を変換します(型の変換を参照)。乗算演算子( *
)は、オペランドとして数値を必要とします。しかし、"3" * "5"
という式は、全く問題ありません。JavaScript がオペランドを数値に変換するからです。この式の値は 15
になります。文字列の "15"
ではありません。また、JavaScript では、値は「 true
と評価されるもの」と「 false
と評価されるもの」のどちらかになります。したがって、オペランドとして論理値が必要な演算子でも、任意の型のオペランドに対して問題なく動作します。
オペランドの型によって働きが変わる演算子もいくつかあります。例えば、+
演算子の場合、オペランドの型が数値なら加算を実行しますが、オペランドの型が文字列であれば連結処理を行います。同じように、<
などの比較演算子は、オペランドの型によって、数値の大小で比較したり、アルファベット順で比較したりします。個々の演算子について説明の中で、必要とする型や、どのような型変換が行われるかについても解説します。
上表を見ると、代入演算子などいくつかの演算子で、オペランドの型として「左辺値」を必要とするものがあります。左辺値とは、昔から使われている用語で、「代入演算子の左側に記述しても問題のない式」という意味です。JavaScript の場合は、変数やオブジェクトのプロパティ、配列の要素が左辺値になります。ECMAScript 仕様では、組み込み関数が左辺値を返してもよいことになっています。ただし、このような処理を行う関数は定義されていません。
2 * 3
のような単純な式を評価したとしても、プログラムの状態は何も変化しません。また、今後行う計算にも、この式の評価は何も影響しません。しかし、式の中には副作用を持つものがあります。このような式は、その後の評価結果を変えてしまう場合があります。代入演算子が最もわかりやすい例でしょう。変数やプロパティに値を代入すれば、今後その値やプロパティを使う式の値が変化します。同じように、インクリメント演算子( ++
)やデクリメント演算子( --
)も暗黙的に代入を行うので副作用が生じます。delete
演算子も副作用があります。プロパティを削除することは、プロパティに undefined
を代入するのとほぼ同じ意味になるからです。
JavaScript では、このほかの演算子に副作用はありません。ただし、関数呼び出し式やオブジェクト生成式は、関数中で前述したような演算子が使われた場合に、副作用を持つことになります。
上表に列挙した演算子は、優先順位の高いものから順に並べています。同じ優先順位のものは、横線を引いてグループに分けています。
演算子が複数ある場合に、どの演算子から演算を行うかは、この優先順位で制御されます。つまり、表の上の方に挙げている優先順位の高い演算子から処理が行われます。
次の式を見てください。
w = x + y*z;
乗算演算子 *
の方が加算演算子 +
より優先順位が高いので、まず乗算が行われ、次に加算が行われます。代入演算子 =
は優先順位が最も低いので、右側の処理が全て終了した後に実行されます。
優先順位を変更したい場合は、括弧( ()
)を使用します。上記の例で、加算を実行してから乗算を実行したい場合は次のようにします。
w = (x + y)*z;
プロパティアクセス式と呼び出し式は、上表のすべての演算子よりも優先順位が高くなります。次の式を見てください。
typeof my.functions[x](y)
typeof
は優先順位が最高の演算子ですが、プロパティアクセスや関数呼び出しがまず実行され、その結果に対して typeof 演算が行われます。
演算子の優先順位がよくわからない場合は、括弧を使用して明示的に評価順序を指定することをお勧めします。とりあえず、まず乗算と除算が行われ、次に加算と減算が行われ、最後に代入処理が行われる、ということだけは覚えておきましょう。
上表で、A(associativity)は演算子の結合性を示しています。A 列の L(left-to-right)は左から右に演算子を結合するという意味です。同様に R(right-to-left)は右から左へ演算子を結合するという意味です。このようなルールは、優先順位が同じ演算子の実行順序を決めるときに必要になります。L が指定されていれば、左から右へ順に処理されます。次の例を見てください。
w = x - y - z;
減算演算子は左から右に処理されるので、この式は次のように括弧を指定したのと同じです。
w = ((x - y) - z);
一方、次の例を見てください。
x = ~-y;
w = x = y = z;
q = a?b:c?d:e?f:g;
単項演算子や代入演算子、条件演算子は右から左へ処理されるので、これらの式は次のように記述したのと同じです。
x = ~(-y);
w = (x = (y = z));
q = a?b:(c?d:(e?f:g));
演算子の優先順位と結合性により、複雑な式の中での演算子の処理順序が決められます。しかし、優先順位と結合性は、個々の式が評価される順序までは指定しません。JavaScript では、常に式の評価は左から右に行われます。例えば、w = x + y * z
という式の場合、まず w
が評価され、その後、x, y, z
の順序で評価が行われます。そして、y
と z
の値が乗算され、x
の値と加算され、w
という式で指定された変数やプロパティに代入が行われます。式に対して、括弧を追加すれば、乗算や加算、代入の順序を変更できますが、評価の順序(左から右)は変更できません。
評価の順序が問題となるのは、評価される式の中に副作用を持ち、この副作用がほかの式に影響を及ぼす場合だけです。もしも先程の例で、z
式で使う変数が x
式の中でインクリメントされる場合、x
が z
よりも前に評価されるという事実は重要になります。