演算子の優先順位
演算子の優先順位は、演算子が互いにどのように解釈されるかを決定します。優先度の高い演算子は、優先度の低い演算子のオペランドになります。
試してみましょう
優先度と結合性
以下の表現で記述できる式を考えてみましょう。なお、OP1
と OP2
は演算子に置き換わります。
a OP1 b OP2 c
OP1
と OP2
の優先順位(下記の一覧表を参照)が異なる場合は、優先順位の高い演算子が先に実行され、結合性は関係ありません。コードの中で加算が先に書かれているにもかかわらず、乗算の方が加算よりも優先順位が高く、先に実行されていることを確認してください。
console.log(3 + 10 * 2); // 23 を出力
console.log(3 + 10 * 2); // 括弧の優先順位が高いので、23 を出力
console.log((3 + 10) * 2); // 括弧が順位を変更するので 26 を出力
左結合(左から右)は (a OP1 b) OP2 c
のように処理されることであり、右結合(右から左)は a OP1 (b OP2 c)
のように解釈されることです。代入演算子は右結合なので、このように書くことができます。
a = b = 5; // a = (b = 5); と書いたのと同じ
これで、a
と b
が 5 の値を得るという期待通りの結果を得ることができます。これは代入演算子が代入した値を返すためです。まず b
に 5 が設定されます。そして a
にも、代入演算子の右オペランドである b = 5
が返す 5 が設定されるのです。
他の例として、べき乗演算子だけが右結合性を持ちますが、他の算術演算子は左結合性を持ちます。興味深いのは、結合性や優先順位に関係なく、評価の順序は常に左から右になることです。
コード | 出力結果 |
js
|
Evaluating the left side Evaluating the right side 3 |
js
|
Evaluating the left side Evaluating the right side 8 |
結合性の違いは、同じ優先順位の演算子が複数存在する場合に現れます。上の例のように演算子が一つだけの場合や、演算子の優先順位が異なる場合は、結合性は出力に影響を与えません。下の例では、同じ演算子が複数使われている場合に、結合性が出力結果にどのような影響を与えるかを見てみましょう。
コード | 出力結果 |
js
|
Evaluating the left side Evaluating the middle side Evaluating the right side 1 |
js
|
Evaluating the left side Evaluating the middle side Evaluating the right side 512 |
js
|
Evaluating the left side Evaluating the middle side Evaluating the right side 64 |
上記のコードを見てください。6 / 3 / 2
は、除算が左結合なので (6 / 3) / 2
と同じになります。一方で、べき乗は右結合なので、2 ** 3 ** 2
は 2 ** (3 ** 2)
と同じになります。したがって、 (2 ** 3) ** 2
とすると上記の表にある通り、演算順序が変わって結果が 64 になります。
優先順位は結合度よりも優先されることを忘れないでください。そのため、割り算とべき乗を交ぜた場合、べき乗は割り算よりも先に計算されます。例えば 2 ** 3 / 3 ** 2
の結果は 0.8888888888888888 となります。これは (2 ** 3) / (3 ** 2)
と同じだからです。
グループ化と短絡評価の注意
下記の表では、グループ化が最上位の優先順位を持つものとして挙げられています。しかし、特に短絡が発生する場合は、グループ化記号 ( … )
の中の式が最初に評価されるとは限りません。
短絡評価は、条件付き評価を表す用語です。例えば、a && (b + c)
という式において、a
が偽値である場合、従属式である (b + c)
は括弧で囲まれていても評価されません。この論理的分離演算子 ("OR") は「短絡的」といえるでしょう。論理的分離演算子の他にも、ほかに短絡が発生する演算子には、論理的結合 ("AND") 演算子、Null 合体演算子、オプション連鎖演算子、条件演算子があります。以下に例を示します。
a || b * c; // 最初に `a` を評価し、 `a` が「真値」であれば `a` を出力
a && b < c; // 最初に `a` を評価し、 `a` が「偽値」であれば `a` を出力
a ?? (b || c); // 最初に `a` を評価し、 `a` が `null` または `undefined` でなければ `a` を出力
a?.b.c; // 最初に `a` を評価し、 `a` が `null` または `undefined` であれば `undefined` を出力
例
3 > 2 && 2 > 1;
// true を返す
3 > 2 > 1;
// 結果は false となる。3 > 2 は true であり、true は
// 不等号で 1 に変換されるため、true > 1 は 1 > 1 となり、
// false となる。(3 > 2) > 1 のように括弧を付けると明確になる。
一覧表
以下の表は優先順位の最も高いもの (19) から最も低いもの (1) の順に並べられています。
なお、スプレッド構文はこの表から除外しています。 — 理由は Stack Overflow の回答を引用します。「スプレッド構文は演算子ではなく、優先度はありません。これは配列リテラルと関数呼び出し(およびオブジェクトリテラル)の構文の一部です。」
優先順位 | 演算子の種類 | 結合性 | 演算子 |
---|---|---|---|
19 | グループ化 | なし | ( … ) |
18 | メンバーへのアクセス | 左から右 | … . … |
計算値によるメンバーへのアクセス | 左から右 | … [ … ] |
|
new (引数リスト付き) |
なし | new … ( … ) |
|
関数呼び出し | 左から右 |
… ( … )
|
|
オプショナルチェーン | 左から右 | ?. |
|
17 |
new (引数リストなし)
|
右から左 | new … |
16 | 後置インクリメント | なし | … ++ |
後置デクリメント | … -- |
||
15 | 論理 NOT (!) | 右から左 | ! … |
ビット単位 NOT (~) | ~ … |
||
単項 + | + … |
||
単項 - | - … |
||
前置インクリメント | ++ … |
||
前置デクリメント | -- … |
||
typeof |
typeof … |
||
void |
void … |
||
delete |
delete … |
||
await |
await … |
||
14 | べき乗 (**) | 右から左 | … ** … |
13 | 乗算 (*) | 左から右 | … * … |
除算 (/) | … / … |
||
剰余 (%) | … % … |
||
12 | 加算 (+) | 左から右 | … + … |
減算(-) | … - … |
||
11 | 左ビットシフト (<<) | 左から右 | … << … |
右ビットシフト | … >> … |
||
符号なし右ビットシフト (>>>) | … >>> … |
||
10 | 小なり (<) | 左から右 | … < … |
小なりイコール (<=) | … <= … |
||
大なり (>) | … > … |
||
大なりイコール (>=) | … >= … |
||
in |
… in … |
||
instanceof |
… instanceof … |
||
9 | 等価 (==) | 左から右 | … == … |
不等価 (!=) | … != … |
||
厳密等価 (===) | … === … |
||
厳密不等価 (!==) | … !== … |
||
8 | ビット単位 AND (&) | 左から右 | … & … |
7 | ビット単位 XOR (^) | 左から右 | … ^ … |
6 | ビット単位 OR (|) | 左から右 | … | … |
5 | 論理 AND (&&) | 左から右 | … && … |
4 | 論理 OR (||) | 左から右 | … || … |
Null 合体 (??) | 左から右 | … ?? … |
|
3 | 条件(三項)演算子 | 右から左 | … ? … : … |
2 | 代入 | 右から左 | … = … |
… += … |
|||
… -= … |
|||
… **= … |
|||
… *= … |
|||
… /= … |
|||
… %= … |
|||
… <<= … |
|||
… >>= … |
|||
… >>>= … |
|||
… &= … |
|||
… ^= … |
|||
… |= … |
|||
… &&= … |
|||
… ||= … |
|||
… ??= … |
|||
yield |
右から左 | yield … |
|
yield* |
yield* … |
||
1 | カンマ / シーケンス | 左から右 | … , … |