Operator precedence
Operator precedence determines how operators are parsed concerning each other. Operators with higher precedence become the operands of operators with lower precedence.
Try it
Precedence and associativity
Consider an expression describable by the representation below, where both OP1
and OP2
are fillintheblanks for OPerators.
a OP1 b OP2 c
The combination above has two possible interpretations:
(a OP1 b) OP2 c a OP1 (b OP2 c)
Which one the language decides to adopt depends on the identity of OP1
and OP2
.
If OP1
and OP2
have different precedence levels (see the table below), the operator with the higher precedence goes first and associativity does not matter. Observe how multiplication has higher precedence than addition and executed first, even though addition is written first in the code.
console.log(3 + 10 * 2); // 23
console.log(3 + (10 * 2)); // 23, because parentheses here are superfluous
console.log((3 + 10) * 2); // 26, because the parentheses change the order
Within operators of the same precedence, the language groups them by associativity. Leftassociativity (lefttoright) means that it is interpreted as (a OP1 b) OP2 c
, while rightassociativity (righttoleft) means it is interpreted as a OP1 (b OP2 c)
. Assignment operators are rightassociative, so you can write:
a = b = 5; // same as writing a = (b = 5);
with the expected result that a
and b
get the value 5. This is because the assignment operator returns the value that is assigned. First, b
is set to 5. Then the a
is also set to 5 — the return value of b = 5
, a.k.a. right operand of the assignment.
As another example, the unique exponentiation operator has rightassociativity, whereas other arithmetic operators have leftassociativity.
const a = 4 ** 3 ** 2; // Same as 4 ** (3 ** 2); evaluates to 262144
const b = 4 / 3 / 2; // Same as (4 / 3) / 2; evaluates to 0.6666...
Operators are first grouped by precedence, and then, for adjacent operators that have the same precedence, by associativity. So, when mixing division and exponentiation, the exponentiation always comes before the division. For example, 2 ** 3 / 3 ** 2
results in 0.8888888888888888 because it is the same as (2 ** 3) / (3 ** 2)
.
For prefix unary operators, suppose we have the following pattern:
OP1 a OP2 b
where OP1
is a prefix unary operator and OP2
is a binary operator. If OP1
has higher precedence than OP2
, then it would be grouped as (OP1 a) OP2 b
; otherwise, it would be OP1 (a OP2 b)
.
const a = 1;
const b = 2;
typeof a + b; // Equivalent to (typeof a) + b; result is "number2"
If the unary operator is on the second operand:
a OP2 OP1 b
Then the binary operator OP2
must have lower precedence than the unary operator OP1
for it to be grouped as a OP2 (OP1 b)
. For example, the following is invalid:
function* foo() {
a + yield 1;
}
Because +
has higher precedence than yield
, this would become (a + yield) 1
— but because yield
is a reserved word in generator functions, this would be a syntax error. Luckily, most unary operators have higher precedence than binary operators and do not suffer from this pitfall.
If we have two prefix unary operators:
OP1 OP2 a
Then the unary operator closer to the operand, OP2
, must have higher precedence than OP1
for it to be grouped as OP1 (OP2 a)
. It's possible to get it the other way and end up with (OP1 OP2) a
:
async function* foo() {
await yield 1;
}
Because await
has higher precedence than yield
, this would become (await yield) 1
, which is awaiting an identifier called yield
, and a syntax error. Similarly, if you have new !A;
, because !
has lower precedence than new
, this would become (new !) A
, which is obviously invalid. (This code looks nonsensical to write anyway, since !A
always produces a boolean, not a constructor function.)
For postfix unary operators (namely, ++
and 
), the same rules apply. Luckily, both operators have higher precedence than any binary operator, so the grouping is always what you would expect. Moreover, because ++
evaluates to a value, not a reference, you can't chain multiple increments together either, as you may do in C.
let a = 1;
a++++; // SyntaxError: Invalid lefthand side in postfix operation.
Operator precedence will be handled recursively. For example, consider this expression:
1 + 2 ** 3 * 4 / 5 >> 6
First, we group operators with different precedence by decreasing levels of precedence.
 The
**
operator has the highest precedence, so it's grouped first.  Looking around the
**
expression, it has*
on the right and+
on the left.*
has higher precedence, so it's grouped first.*
and/
have the same precedence, so we group them together for now.  Looking around the
*
//
expression grouped in 2, because+
has higher precedence than>>
, the former is grouped.
(1 + ( (2 ** 3) * 4 / 5) ) >> 6
// │ │ └─ 1. ─┘ │ │
// │ └────── 2. ───────┘ │
// └────────── 3. ──────────┘
Within the *
//
group, because they are both leftassociative, the left operand would be grouped.
(1 + ( ( (2 ** 3) * 4 ) / 5) ) >> 6
// │ │ │ └─ 1. ─┘ │ │ │
// │ └─│─────── 2. ───│────┘ │
// └──────│───── 3. ─────│──────┘
// └───── 4. ─────┘
Note that operator precedence and associativity only affect the order of evaluation of operators (the implicit grouping), but not the order of evaluation of operands. The operands are always evaluated from lefttoright. The higherprecedence expressions are always evaluated first, and their results are then composed according to the order of operator precedence.
function echo(name, num) {
console.log(`Evaluating the ${name} side`);
return num;
}
// Exponentiation operator (**) is rightassociative,
// but all call expressions (echo()), which have higher precedence,
// will be evaluated before ** does
console.log(echo("left", 4) ** echo("middle", 3) ** echo("right", 2));
// Evaluating the left side
// Evaluating the middle side
// Evaluating the right side
// 262144
// Exponentiation operator (**) has higher precedence than division (/),
// but evaluation always starts with the left operand
console.log(echo("left", 4) / echo("middle", 3) ** echo("right", 2));
// Evaluating the left side
// Evaluating the middle side
// Evaluating the right side
// 0.4444444444444444
If you are familiar with binary trees, think about it as a postorder traversal.
/ ┌────────┴────────┐ echo("left", 4) ** ┌────────┴────────┐ echo("middle", 3) echo("right", 2)
After all operators have been properly grouped, the binary operators would form a binary tree. Evaluation starts from the outermost group — which is the operator with the lowest precedence (/
in this case). The left operand of this operator is first evaluated, which may be composed of higherprecedence operators (such as a call expression echo("left", 4)
). After the left operand has been evaluated, the right operand is evaluated in the same fashion. Therefore, all leaf nodes — the echo()
calls — would be visited lefttoright, regardless of the precedence of operators joining them.
Shortcircuiting
In the previous section, we said "the higherprecedence expressions are always evaluated first" — this is generally true, but it has to be amended with the acknowledgement of shortcircuiting, in which case an operand may not be evaluated at all.
Shortcircuiting is jargon for conditional evaluation. For example, in the expression a && (b + c)
, if a
is falsy, then the subexpression (b + c)
will not even get evaluated, even if it is grouped and therefore has higher precedence than &&
. We could say that the logical AND operator (&&
) is "shortcircuited". Along with logical AND, other shortcircuited operators include logical OR (
), nullish coalescing (??
), and optional chaining (?.
).
a  (b * c); // evaluate `a` first, then produce `a` if `a` is "truthy"
a && (b < c); // evaluate `a` first, then produce `a` if `a` is "falsy"
a ?? (b  c); // evaluate `a` first, then produce `a` if `a` is not `null` and not `undefined`
a?.b.c; // evaluate `a` first, then produce `undefined` if `a` is `null` or `undefined`
When evaluating a shortcircuited operator, the left operand is always evaluated. The right operand will only be evaluated if the left operand cannot determine the result of the operation.
Note: The behavior of shortcircuiting is baked in these operators. Other operators would always evaluate both operands, regardless if that's actually useful — for example, NaN * foo()
will always call foo
, even when the result would never be something other than NaN
.
The previous model of a postorder traversal still stands. However, after the left subtree of a shortcircuiting operator has been visited, the language will decide if the right operand needs to be evaluated. If not (for example, because the left operand of 
is already truthy), the result is directly returned without visiting the right subtree.
Consider this case:
function A() { console.log('called A'); return false; }
function B() { console.log('called B'); return false; }
function C() { console.log('called C'); return true; }
console.log(C()  B() && A());
// Logs:
// called C
// true
Only C()
is evaluated, despite &&
having higher precedence. This does not mean that 
has higher precedence in this case — it's exactly because (B() && A())
has higher precedence that causes it to be neglected as a whole. If it's rearranged as:
console.log(A() && C()  B());
// Logs:
// called A
// called B
// false
Then the shortcircuiting effect of &&
would only prevent C()
from being evaluated, but because A() && C()
as a whole is false
, B()
would still be evaluated.
However, note that shortcircuiting does not change the final evaluation outcome. It only affects the evaluation of operands, not how operators are grouped — if evaluation of operands doesn't have side effects (for example, logging to the console, assigning to variables, throwing an error), shortcircuiting would not be observable at all.
The assignment counterparts of these operators (&&=
, =
, ??=
) are shortcircuited as well. They are shortcircuited in a way that the assignment does not happen at all.
Table
The following table lists operators in order from highest precedence (18) to lowest precedence (1).
Several general notes about the table:
 Not all syntax included here are "operators" in the strict sense. For example, spread
...
and arrow=>
are typically not regarded as operators. However, we still included them to show how tightly they bind compared to other operators/expressions.  Some operators have certain operands that require expressions narrower than those produced by higherprecedence operators. For example, the righthand side of member access
.
(precedence 17) must be an identifier instead of a grouped expression. The lefthand side of arrow=>
(precedence 2) must be an arguments list or a single identifier instead of some random expression.  Some operators have certain operands that accept expressions wider than those produced by higherprecedence operators. For example, the bracketenclosed expression of bracket notation
[ … ]
(precedence 17) can be any expression, even comma (precedence 1) joined ones. These operators act as if that operand is "automatically grouped". In this case we will omit the associativity.
Precedence  Associativity  Individual operators  Notes 

18: grouping  n/a  Grouping(x) 
[1] 
17: access and call  lefttoright  Member accessx.y 
[2] 
Optional chainingx?.y 

n/a  Computed member accessx[y] 
[3]  
new with argument listnew x(y) 
[4]  
Function callx(y) 

import(x) 

16: new  n/a  new without argument listnew x 

15: postfix operators  n/a  Postfix incrementx++ 
[5] 
Postfix decrementx 

14: prefix operators  n/a  Prefix increment++x 
[6] 
Prefix decrementx 

Logical NOT!x 

Bitwise NOT~x 

Unary plus+x 

Unary negationx 

typeof x 

void x 

delete x 
[7]  
await x 

13: exponentiation  righttoleft  Exponentiationx ** y 
[8] 
12: multiplicative operators  lefttoright  Multiplicationx * y 

Divisionx / y 

Remainderx % y 

11: additive operators  lefttoright  Additionx + y 

Subtractionx  y 

10: bitwise shift  lefttoright  Left shiftx << y 

Right shiftx >> y 

Unsigned right shiftx >>> y 

9: relational operators  lefttoright  Less thanx < y 

Less than or equalx <= y 

Greater thanx > y 

Greater than or equalx >= y 

x in y 

x instanceof y 

8: equality operators  lefttoright  Equalityx == y 

Inequalityx != y 

Strict equalityx === y 

Strict inequalityx !== y 

7: bitwise AND  lefttoright  Bitwise ANDx & y 

6: bitwise XOR  lefttoright  Bitwise XORx ^ y 

5: bitwise OR  lefttoright  Bitwise ORx  y 

4: logical AND  lefttoright  Logical ANDx && y 

3: logical OR, nullish coalescing  lefttoright  Logical ORx  y 

Nullish coalescing operatorx ?? y 
[9]  
2: assignment and miscellaneous  righttoleft  Assignmentx = y 
[10] 
Addition assignmentx += y 

Subtraction assignmentx = y 

Exponentiation assignmentx **= y 

Multiplication assignmentx *= y 

Division assignmentx /= y 

Remainder assignmentx %= y 

Left shift assignmentx <<= y 

Right shift assignmentx >>= y 

Unsigned right shift assignmentx >>>= y 

Bitwise AND assignmentx &= y 

Bitwise XOR assignmentx ^= y 

Bitwise OR assignmentx = y 

Logical AND assignmentx &&= y 

Logical OR assignmentx = y 

Nullish coalescing assignmentx ??= y 

righttoleft  Conditional (ternary) operatorx ? y : z 
[11]  
righttoleft  Arrowx => y 
[12]  
n/a  yield x 

yield* x 

Spread...x 
[13]  
1: comma  lefttoright  Comma operatorx, y 
Notes:
 The operand can be any expression.
 The "righthand side" must be an identifier.
 The "righthand side" can be any expression.
 The "righthand side" is a commaseparated list of any expression with precedence > 1 (i.e. not comma expressions).
 The operand must be a valid assignment target (identifier or property access). Its precedence means
new Foo++
is(new Foo)++
(a syntax error) and notnew (Foo++)
(a TypeError: (Foo++) is not a constructor).  The operand must be a valid assignment target (identifier or property access).
 The operand cannot be an identifier or a private property access.
 The lefthand side cannot have precedence 14.
 The operands cannot be a logical OR

or logical AND&&
operator without grouping.  The "lefthand side" must be a valid assignment target (identifier or property access).
 The associativity means the two expressions after
?
are implicitly grouped.  The "lefthand side" is a single identifier or a parenthesized parameter list.
 Only valid inside object literals, array literals, or argument lists.