JavaScript has dozens of operators across several precedence levels, and a misread of which binds first is a classic source of bugs. This reference lists each operator with its precedence, associativity, and a worked example so you can predict exactly how an expression evaluates.
How it works
Every operator sits at a precedence level: when an expression has multiple
operators without parentheses, the higher-precedence one is applied first. So
2 + 3 * 4 multiplies before adding because * outranks +.
When two operators share a precedence level, associativity breaks the tie.
Most binary operators are left-associative, grouping left to right, so
10 - 4 - 2 is (10 - 4) - 2. Assignment, exponentiation (**), and the
conditional ?: are right-associative, so a = b = c and 2 ** 3 ** 2 group
from the right.
Operators that trip people up
Logical vs nullish coalescing
|| and ?? are similar but behave differently on falsy values:
0 || 5 // 5 — because 0 is falsy
0 ?? 5 // 0 — because 0 is not null or undefined
"" || "hi" // "hi"
"" ?? "hi" // ""
Use ?? when 0, empty string, or false are legitimate values you want to preserve. Mixing ?? with || or && without parentheses is a syntax error by design.
Optional chaining ?.
?. short-circuits to undefined if the value on its left is null or undefined, instead of throwing:
const name = user?.profile?.name; // undefined if user or profile is null
func?.(); // calls func only if it is a function
arr?.[0]; // safe index access
Logical assignment operators (ES2021)
These combine a logical check with assignment in one step:
a ||= b // a = a || b — assign b if a is falsy
a &&= b // a = a && b — assign b if a is truthy
a ??= b // a = a ?? b — assign b only if a is null or undefined
Exponentiation associativity
** is right-associative, which matches mathematical convention but differs from every other binary arithmetic operator:
2 ** 3 ** 2 // 512 — parsed as 2 ** (3 ** 2), not (2 ** 3) ** 2
Tips and examples
2 + 3 * 4 // 14, not 20
2 ** 3 ** 2 // 512 — right-associative, so 2 ** (3 ** 2)
true || false && false // true — && binds tighter than ||
a ?? b || c // SyntaxError — must parenthesize ?? with || or &&
typeof null // "object" — a legacy quirk, not a bug in typeof
When in doubt, add parentheses — they are free and make intent explicit. The
nullish (??) and logical (||, &&) operators may not be mixed without
parentheses by design, precisely because their precedence relationship surprised
people. The comma operator (lowest precedence of all) evaluates both operands
and returns the last — rarely needed intentionally but easy to introduce accidentally in a for-loop header.