JavaScript Operator Precedence: Mastering the Rules of Evaluation Order
Remember the "multiply and divide before add and subtract" rule from elementary math? In JavaScript, operators have similar precedence rules. Just like traffic rules determine who goes first, operator precedence determines which operation executes first in complex expressions. Understanding these rules helps you write correct and readable code.
Why Do We Need Precedence?
Look at a simple example:
let result = 3 + 4 * 5;
console.log(result); // 23, not 35!Why is it 23 and not 35? Because multiplication (*) has higher precedence than addition (+), so 4 * 5 = 20 is calculated first, then 3 + 20 = 23.
If we want to calculate addition first, we need to use parentheses:
let result = (3 + 4) * 5;
console.log(result); // 35Precedence Table (High to Low)
Here are the commonly used operators sorted by precedence, with higher numbers indicating higher precedence:
// Precedence 20: Grouping (parentheses)
(expression)
// Precedence 19: Member access, function call
obj.property
obj[property]
func(args)
new Constructor()
// Precedence 17: Postfix increment/decrement
x++
x--
// Precedence 16: Logical NOT, unary operators, prefix increment/decrement
!x
+x
-x
typeof x
++x
--x
// Precedence 15: Exponentiation
**
// Precedence 14: Multiplication, division, modulo
*
/
%
// Precedence 13: Addition, subtraction
+
-
// Precedence 12: Bitwise shift
<<
>>
>>>
// Precedence 11: Relational operators
<
<=
>
>=
in
instanceof
// Precedence 10: Equality operators
==
!=
===
!==
// Precedence 9-5: Bitwise operators
&
^
|
// Precedence 6: Logical AND
&&
// Precedence 5: Logical OR
||
// Precedence 4: Nullish coalescing
??
// Precedence 4: Ternary operator
? :
// Precedence 3: Assignment operators
=
+=
-=
*=
/=
etc.
// Precedence 1: Comma operator
,Common Precedence Examples
Arithmetic Operator Precedence
// Multiplication/division before addition/subtraction
console.log(2 + 3 * 4); // 14 (first 3*4=12, then 2+12=14)
console.log(10 - 6 / 2); // 7 (first 6/2=3, then 10-3=7)
console.log(8 / 2 + 3); // 7 (first 8/2=4, then 4+3=7)
// Same precedence evaluates left to right
console.log(10 - 3 - 2); // 5 (first 10-3=7, then 7-2=5)
console.log(20 / 4 / 2); // 2.5 (first 20/4=5, then 5/2=2.5)
// Exponentiation before multiplication/division
console.log(2 * 3 ** 2); // 18 (first 3**2=9, then 2*9=18)
console.log(2 ** 3 * 4); // 32 (first 2**3=8, then 8*4=32)
// Use parentheses to change precedence
console.log((2 + 3) * 4); // 20
console.log(2 * (3 + 4)); // 14Comparison and Logical Operators
// Comparison before logical operations
let age = 25;
let hasLicense = true;
// First execute age >= 18, get true, then execute true && hasLicense
console.log(age >= 18 && hasLicense); // true
// && before ||
console.log(true || (false && false)); // true (first false && false = false, then true || false = true)
console.log((true || false) && false); // false (change precedence)
// ! before &&
let isWeekend = false;
let isHoliday = false;
console.log(!isWeekend && !isHoliday); // true (first execute both !)
// Practical application
let score = 85;
let attendance = 90;
let passed = score >= 80 && attendance >= 85;
console.log(passed); // trueAssignment Operators Have Lowest Precedence
Assignment operators have very low precedence, almost always executing last:
let a, b, c;
// First calculate the right expression, then assign
a = 5 + 3; // First 5+3=8, then assign to a
console.log(a); // 8
b = 10 > 5; // First compare, get true, then assign to b
console.log(b); // true
c = 2 + 3 * 4 > 10; // First 3*4=12, then 2+12=14, then 14>10=true, finally assign
console.log(c); // trueUnary Operator Precedence
Unary operators have high precedence:
// typeof has higher precedence than arithmetic operations
console.log(typeof 5 + 3); // "number3" (first typeof 5 = "number", then concatenate)
console.log(typeof (5 + 3)); // "number" (change precedence)
// ! has very high precedence
console.log(!true || false); // false (first !true=false, then false || false)
// Increment/decrement
let x = 5;
let y = ++x * 2; // First ++x = 6, then 6*2=12
console.log(y); // 12
console.log(x); // 6
let a = 5;
let b = a++ * 2; // First use a=5 for calculation 5*2=10, then increment a
console.log(b); // 10
console.log(a); // 6Associativity
When operators have the same precedence, associativity determines the evaluation order. Most operators are left-associative (left to right), while a few are right-associative (right to left).
Left-Associativity
// Arithmetic operators: left to right
console.log(10 - 5 - 2); // 3 ((10 - 5) - 2 = 5 - 2 = 3)
console.log(20 / 4 / 2); // 2.5 ((20 / 4) / 2 = 5 / 2 = 2.5)
// Addition: left to right
console.log(1 + 2 + 3 + 4); // 10 (((1 + 2) + 3) + 4)
// String concatenation
console.log("a" + "b" + "c"); // "abc" (("a" + "b") + "c")
console.log(1 + 2 + "3"); // "33" ((1 + 2) + "3" = 3 + "3")
console.log("1" + 2 + 3); // "123" (("1" + 2) + 3 = "12" + 3)Right-Associativity
// Assignment operators: right to left
let a, b, c;
a = b = c = 10;
// Execution order: c = 10, then b = 10, finally a = 10
console.log(a, b, c); // 10 10 10
// Ternary operator: right to left
let result = true ? 1 : false ? 2 : 3;
// Execution order: false ? 2 : 3 = 3, then true ? 1 : 3 = 1
console.log(result); // 1
// Exponentiation: right to left (this is special!)
console.log(2 ** (3 ** 2)); // 512 (2 ** (3 ** 2) = 2 ** 9 = 512)
console.log((2 ** 3) ** 2); // 64 (change associativity)Complex Expression Analysis
Let's analyze some complex expressions:
Example 1: Mixed Operations
let result = 5 + 3 * 2 - 4 / 2;
// Step-by-step execution:
// 1. 3 * 2 = 6 (multiplication has precedence)
// 2. 4 / 2 = 2 (division has precedence)
// 3. 5 + 6 = 11 (addition)
// 4. 11 - 2 = 9 (subtraction)
console.log(result); // 9Example 2: Logic and Comparison
let x = 10;
let y = 20;
let z = 30;
let result = (x < y && y < z) || z < x;
// Step-by-step execution:
// 1. x < y => 10 < 20 = true
// 2. y < z => 20 < 30 = true
// 3. z < x => 30 < 10 = false
// 4. true && true = true
// 5. true || false = true
console.log(result); // trueExample 3: Assignment and Increment
let a = 5;
let b = a++ + ++a;
// Step-by-step execution:
// 1. a++ uses current value 5, then a becomes 6
// 2. ++a first increments (a becomes 7), then uses 7
// 3. 5 + 7 = 12
// 4. Assign to b
console.log(b); // 12
console.log(a); // 7Example 4: Conditional and Logic
let score = 85;
let result = score >= 90 ? "A" : score >= 80 ? "B" : "C";
// Step-by-step execution (right to left):
// 1. Inner condition: score >= 80 ? "B" : "C"
// 85 >= 80 is true, so gets "B"
// 2. Outer condition: score >= 90 ? "A" : "B"
// 85 >= 90 is false, so gets "B"
console.log(result); // "B"Practical Programming Advice
1. Use Parentheses to Enhance Readability
Even if you know precedence rules, it's recommended to use parentheses to make code more readable:
// Not clear enough
let result1 = a + b * c - d / e;
// Clearer
let result2 = a + b * c - d / e;
// Complex logic must use parentheses
let canAccess = (isAdmin || isModerator) && !isBanned;2. Avoid Overly Complex Expressions
// ❌ Bad: too complex
let result = (a++ + (++b * c--) / --d && e > f) || g <= h ? i : j;
// ✅ Good: break into multiple steps
let temp1 = a++ + (++b * c--) / --d;
let temp2 = temp1 && e > f;
let result = temp2 || g <= h ? i : j;
// Or better: completely separate
let multiplied = ++b * c--;
let divided = multiplied / --d;
let sum = a++ + divided;
let condition1 = sum && e > f;
let condition2 = condition1 || g <= h;
let result = condition2 ? i : j;3. Beware of Type Conversion Pitfalls
// String concatenation before number addition
console.log(1 + 2 + "3"); // "33"
console.log("1" + 2 + 3); // "123"
console.log("1" + (2 + 3)); // "15"
// Use parentheses to clarify intent
let total = price + tax; // Number addition
let display = "Total: $" + (price + tax); // Calculate first, then concatenate4. Be Careful with Short-Circuit Evaluation
// && short-circuit behavior
let user = null;
let name = user && user.name; // Safe: user is null, won't access name
// But don't overuse
let value = obj && obj.prop && obj.prop.value; // Poor readability
// Use optional chaining for clarity
let value = obj?.prop?.value;5. Assignment Return Values
Assignment operators return the assigned value, which can be used for chained assignment, but be careful:
// Chained assignment
let a = (b = c = 0); // All become 0
// Assignment in conditions (easy to confuse)
if ((x = 10)) {
// This is assignment! Not comparison!
console.log("Always true");
}
// Should be:
if (x === 10) {
console.log("Compare");
}
// Intentional conditional assignment should use parentheses
if ((match = text.match(/pattern/))) {
console.log(match);
}Precedence Memory Tricks
Basic Rules
- Parentheses Highest:
()always evaluated first - Member Access and Function Call:
.,[],() - Unary Operators:
!,typeof,++,--,+,- - Exponentiation:
**(right-associative) - Multiplication, Division, Modulo:
*,/,% - Addition, Subtraction:
+,- - Comparison:
<,>,<=,>= - Equality:
==,!=,===,!== - Logical AND:
&& - Logical OR:
|| - Ternary:
? : - Assignment:
=,+=, etc. (right-associative)
Practical Principles
You don't need to remember all precedences, just the commonly used ones:
- Multiplication/division before addition/subtraction
- Comparison before logic
&&before||- Assignment last
- Use parentheses when unsure
Common Errors
Error 1: Forgetting Operator Precedence
// Wrong
if (type === "user" || "admin") {
// Always true!
// ...
}
// Correct
if (type === "user" || type === "admin") {
// ...
}Error 2: Prefix/Postfix Increment Confusion
let i = 5;
// Easy to confuse
let a = i++ * 2; // a = 10, i = 6
let b = ++i * 2; // b = 14, i = 7
// Better to write separately
i++;
let a = i * 2; // ClearerError 3: Chained Comparisons
// JavaScript doesn't support chained comparisons
let x = 5;
console.log(1 < x < 10); // true, but not what you think!
// Actual execution:
// 1 < x => 1 < 5 => true
// true < 10 => 1 < 10 => true
// Correct approach
console.log(1 < x && x < 10); // trueSummary
Operator precedence is fundamental to JavaScript expression evaluation. Understanding it helps avoid many subtle bugs.
Key Points Recap:
- Precedence determines execution order of different operators
- Associativity determines execution order of same-precedence operators
- Most operators are left-associative, assignment, ternary, and exponentiation are right-associative
- Parentheses
()have the highest precedence and can change execution order - Common precedence: multiplication/division > addition/subtraction > comparison > logical AND > logical OR > assignment
- For code readability, use parentheses to clarify intent
- Avoid writing overly complex expressions
- When unsure, consult documentation or use parentheses