JavaScriptでは、多くの場合演算子を使ってプログラムを作成していきます。
演算子には、優先順位が決められており、評価される順番が異なります。
今回は、演算子の優先順位について解説していきます。

演算子

演算子とは、演算処理を記号で表したものです。
例えば、「5 + 3」のような足し算で使われる加算演算子+も演算子の一種です。

let num = 5 + 3; // 加算演算

演算子には他にも沢山の種類があり、どのような処理を行うのかに応じて使い分ける必要があります。
以下は、掛け算を行うための乗算演算子*や、割り算を行うための除算演算子/を使用した例です。

let num1 = 5 * 3; // 乗算演算
let num2 = 5 / 3; // 除算演算

演算子の優先順位

JavaScriptでは、演算子の優先順位が決められており、優先順位の高いものから演算処理が行われます。

演算子の優先順位が異なる場合

次の+*の演算子を使った例を見てください。

let num = 5 + 3 * 10;

この2つの演算で先に+の演算を行った場合、以下のような結果になります。

let num = 5 + 3;
let total = num * 10;

console.log(total); // 80

反対に、先に*の演算を行うとどうなるでしょうか。

let num = 3 * 10;
let total = num + 5;

console.log(total); // 35

+*のどちらの演算を先に行うのかによって、得られる結果が異なることが分かります。

このような場合、演算子の優先順位によって演算が行われる順番が決められます。
乗算の方が加算よりも優先順位が高く評価されるため、式が左から右の順番に処理されるのではなく、先に乗算が実行されます。

let num = 5 + 3 * 10; // 3 * 10を先に評価
console.log(num); // 35

演算子の優先順位が同じ場合

/*の演算子を使った例を見てください。

let num = 12 / 3 * 4;

先に/の演算を行うと以下のような結果になります。

let num = 12 / 3;
let total = num * 4;

console.log(total); // 16

反対に、先に*の演算を行うとこのような結果になります。

let num = 3 * 4;
let total = num / 12;

console.log(total); // 1

こちらも/*のどちらの演算を先に行うのかによって、得られる結果が異なりました。
しかし、除算と乗算の優先順位は同じです。
演算子の優先順位が同じ場合、演算子の結合性に従って演算の順序が決められます。
除算と乗算の結合性は、どちらも左結合のため、除算が先に評価されることになります。

let num = 12 / 3 * 4; // (12 / 3) * 4; と同じ
console.log(num); // 16

グループ化を行った場合

もう一つ例を見てみましょう。
以下は、除算の方が加算よりも優先順位が高いため、除算が先に行われた結果となります。

let num = 10 + 2 / 6; // 2 / 6を先に評価
console.log(num); // 10.333333333333334

では、加算を括弧で囲ってみるとどうなるでしょうか。

let num = (10 + 2) / 6;
console.log(num); // 2

これも演算子の優先順位が関係しています。
括弧の優先順位が高いため、括弧の中の加算が処理されている結果です。
()は、括弧の中にあるものをグループ化するため、グループ化演算子と呼びます。

優先順位の異なる演算子を複数使う場合には、このようにグループ化演算子が用いられることも多いです。

演算子の結合性

優先順位が異なる演算子では、優先順位の高い演算子が先に実行されるため、結合性は特に関係ありません。
しかし、同じ優先順位の演算子が複数存在する場合、それらの結合性によって、処理順序が変わります。

結合性は、左から右に結合する左結合と、右から左に結合する右結合に分かれます。

a + b - c; // 左結合。(a + b) - c; と同じ
a = b = c; // 右結合。a = (b = c); と同じ 

左から右の順で評価される左結合のものがほとんどですが、参考程度に右結合のものもあるということを意識しておくと良いでしょう。

以下は、演算子の優先順位と結合性の一覧です。
上から優先順位が高いものに並んでいます。

優先順位 結合性 演算子 種類
19 なし (…) グループ化
18 … . … メンバへのアクセス
…[…] メンバへのアクセス
なし new…(…) 引数リストあり
…(…) 関数呼び出し
?. オプショナルチュイニング
17 new… 引数リストなし
16 なし …++ 後置インクリメント
なし …– 後置デクリメント
15 ! 論理否定
~ ビット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 …=… 代入 「※1」
1 , カンマ

「※1」の代入には、数多くの種類があるため省略しています。
他にも以下の代入演算子あります。
「+=」「-=」「**=」「*=」「/=」「%=」「<<=」「>>=」「>>>=」「&=」「^=」「|=」「&&=」「||=」「??=」

まとめ

今回は、演算子の優先順位について解説しました。

もちろんプログラムを作成する上では、後に演算子の種類や使い方の知識が必要になりますが、ここでは、演算がどのよう順番で処理されるのかに注目してきました。

JavaScriptでは、複数の演算子を組み合わせながら処理を書いていく場面も多く、演算処理の優先順位によっては、得られる結果が異なるため、これらの仕組みを知っておくことは大切です。

ぜひ参考にしてください。

合わせて読みたい演算子入門シリーズ

第1回:演算子の優先順位(当記事)
第2回:演算子の種類と役割
第3回:算術演算子
第4回:代入演算子
第5回:比較演算子
第6回:論理演算子