演算子の優先順位

演算子の優先順位は、演算子が互いにどのように解釈されるかを決定します。優先度の高い演算子は、優先度の低い演算子のオペランドになります。

試してみましょう

console.log(3 + 4 * 5); // 3 + 20 // Expected output: 23 console.log(4 * 3 ** 2); // 4 * 9 // Expected output: 36 let a; let b; console.log((a = b = 5)); // Expected output: 5 

優先度と結合性

以下の表現で記述できる式を考えてみましょう。なお、OP1OP2 は演算子に置き換わります。

js
a OP1 b OP2 c 

OP1OP2 の優先順位(下記の一覧表を参照)が異なる場合は、優先順位の高い演算子が先に実行され、結合性は関係ありません。コードの中で加算が先に書かれているにもかかわらず、乗算の方が加算よりも優先順位が高く、先に実行されていることを確認してください。

js
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) のように解釈されることです。代入演算子は右結合なので、このように書くことができます。

js
a = b = 5; // a = (b = 5); と書いたのと同じ 

これで、ab が 5 の値を得るという期待通りの結果を得ることができます。これは代入演算子が代入した値を返すためです。まず b に 5 が設定されます。そして a にも、代入演算子の右オペランドである b = 5 が返す 5 が設定されるのです。

他の例として、べき乗演算子だけが右結合性を持ちますが、他の算術演算子は左結合性を持ちます。興味深いのは、結合性や優先順位に関係なく、評価の順序は常に左から右になることです。

コード出力結果
js
function echo(name, num) { console.log("Evaluating the " + name + " side"); return num; } // 除算演算子 (/) の場合 console.log(echo("left", 6) / echo("right", 2)); 
Evaluating the left side Evaluating the right side 3 
js
function echo(name, num) { console.log("Evaluating the " + name + " side"); return num; } // べき乗演算子 (**) の場合 console.log(echo("left", 2) ** echo("right", 3)); 
Evaluating the left side Evaluating the right side 8

結合性の違いは、同じ優先順位の演算子が複数存在する場合に現れます。上の例のように演算子が一つだけの場合や、演算子の優先順位が異なる場合は、結合性は出力に影響を与えません。下の例では、同じ演算子が複数使われている場合に、結合性が出力結果にどのような影響を与えるかを見てみましょう。

コード出力結果
js
function echo(name, num) { console.log("Evaluating the " + name + " side"); return num; } // 除算演算子 (/) の場合 console.log(echo("left", 6) / echo("middle", 2) / echo("right", 3)); 
Evaluating the left side Evaluating the middle side Evaluating the right side 1 
js
function echo(name, num) { console.log("Evaluating the " + name + " side"); return num; } // べき乗演算子 (**) の場合 console.log(echo("left", 2) ** echo("middle", 3) ** echo("right", 2)); 
Evaluating the left side Evaluating the middle side Evaluating the right side 512 
js
function echo(name, num) { console.log("Evaluating the " + name + " side"); return num; } // 左と中央の間のべき乗を括弧で囲んだ場合 console.log((echo("left", 2) ** echo("middle", 3)) ** echo("right", 2));
Evaluating the left side Evaluating the middle side Evaluating the right side 64

上記のコードを見てください。6 / 3 / 2 は、除算が左結合なので (6 / 3) / 2 と同じになります。一方で、べき乗は右結合なので、2 ** 3 ** 22 ** (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 合体演算子、オプション連鎖演算子、条件演算子があります。以下に例を示します。

js
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` を出力 

js
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 … ( … )
関数呼び出し左から右… ( )
オプショナルチェーン左から右?.
17new (引数リストなし) 右から左new …
16後置インクリメントなし… ++
後置デクリメント… --
15論理 NOT (!)右から左! …
ビット単位 NOT (~)~ …
単項 ++ …
単項 -- …
前置インクリメント++ …
前置デクリメント-- …
typeoftypeof …
voidvoid …
deletedelete …
awaitawait …
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カンマ / シーケンス左から右… , …