JavaScript Operators
Operators are the symbols that do work — adding numbers, comparing values, combining booleans, accessing object properties. JavaScript has a rich operator vocabulary including modern goodies like nullish coalescing (??), optional chaining (?.), and logical assignment (||=). This chapter is a guided tour of every category, with the precedence rules that decide what runs first.
Operator Categories
| Category | Examples | Chapter |
|---|---|---|
| Arithmetic | + - * / % ** |
12 |
| Assignment | = += -= *= ??= |
13 |
| Comparison | == === != !== < > <= >= |
this chapter |
| Logical | && \|\| ! ?? |
this chapter |
| Bitwise | & \| ^ ~ << >> >>> |
this chapter |
| String | +, template literals |
14 |
| Type | typeof, instanceof, in |
this chapter |
| Conditional | cond ? a : b (ternary) |
this chapter |
| Member access | . [] ?. |
this chapter |
| Spread / rest | ... |
34 |
Arithmetic — A Quick Preview
Full coverage in chapter 12. The basics:
5 + 3 // 8
5 - 3 // 2
5 * 3 // 15
5 / 3 // 1.6666...
5 % 3 // 2 — remainder
5 ** 3 // 125 — exponentiation
-5 // -5 — unary minus
+true // 1 — unary plus (coerce to number)
Assignment — A Quick Preview
let x = 5;
x += 3; // x = x + 3 → 8
x -= 2; // 6
x *= 2; // 12
x /= 4; // 3
x **= 2; // 9
x %= 5; // 4
Full coverage including &&=, ||=, ??= in chapter 13.
Comparison Operators
Comparison operators return true or false.
5 === 5 // true strict equality
5 === "5" // false
5 == "5" // true loose equality — coerces types
5 !== 5 // false
5 != "5" // false
5 > 3 // true
5 >= 5 // true
"a" < "b" // true lexicographic comparison
=== vs == — Always Use ===
== does type coercion before comparing. The rules are notorious:
0 == "0" // true
0 == false // true
"" == 0 // true
"" == false // true
null == undefined // true
[] == false // true ← yes, really
[] == ![] // true ← absurd
=== does no coercion. Same type, same value, or false.
0 === "0" // false
0 === false // false
null === undefined // false
Tip: Use === and !== everywhere. The single exception: x == null is a useful shorthand for “x is null or undefined”.
Use === for |
Use == for |
|---|---|
| Every value comparison | value == null (matches both null and undefined) |
Logical Operators
&& (AND), || (OR), ! (NOT), and the newer ?? (nullish coalescing).
true && false // false
true || false // true
!true // false
In JavaScript, && and || are short-circuit and return the last evaluated operand — not necessarily a boolean.
"hello" && "world" // "world" (both truthy → returns last)
0 && "world" // 0 (short-circuits on falsy)
null || "fallback" // "fallback"
"first" || "second" // "first" (truthy → returns first)
This makes them useful for defaults:
const name = user.name || "anonymous"; // works, but...
?? — Nullish Coalescing (ES2020)
|| falls back on any falsy value, which is often too aggressive — 0 and "" are real values. ?? only falls back on null or undefined:
const count = inputCount || 10; // ❌ uses 10 when inputCount is 0
const count = inputCount ?? 10; // ✅ uses 10 only when null/undefined
null ?? "x" // "x"
undefined ?? "x" // "x"
0 ?? "x" // 0
"" ?? "x" // ""
false ?? "x" // false
\|\| |
?? |
|
|---|---|---|
Falls back on 0 |
✅ Yes | ❌ No |
Falls back on "" |
✅ Yes | ❌ No |
Falls back on false |
✅ Yes | ❌ No |
Falls back on null |
✅ Yes | ✅ Yes |
Falls back on undefined |
✅ Yes | ✅ Yes |
Use ?? when falsy-but-valid values (0, "", false) should be preserved.
The Ternary Operator
A compact if/else that returns a value.
const access = age >= 18 ? "adult" : "minor";
// Same as:
let access;
if (age >= 18) access = "adult";
else access = "minor";
You can chain them, but it gets ugly fast — switch to if/else once it gets nested:
// ✅ Clear
const tier = score >= 90 ? "A" : score >= 80 ? "B" : "C";
// ❌ Too far — refactor to if/else
const grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : score >= 60 ? "D" : "F";
Member Access — . [] and ?.
How you read properties off objects and arrays:
const user = { name: "Ada", address: { city: "London" } };
user.name // "Ada"
user["name"] // "Ada"
user.address.city // "London"
user["address"]["city"]// "London"
const arr = [10, 20, 30];
arr[1] // 20
?. — Optional Chaining (ES2020)
?. short-circuits to undefined if the thing before it is null or undefined, instead of throwing.
const user = null;
user.name // ❌ TypeError: Cannot read properties of null
user?.name // ✅ undefined
user?.profile?.email // ✅ undefined (no throw)
user?.getOrders?.() // ✅ method call only if it exists
user?.tags?.[0] // ✅ array access only if user.tags exists
| Operator | When to Use |
|---|---|
. |
When you’re sure the object exists |
?. |
When the path may not exist |
?.() |
Method may not be defined |
?.[expr] |
Bracket access on possibly-null object |
Type Operators
typeof 42 // "number"
typeof "hi" // "string"
typeof undefined // "undefined"
[] instanceof Array // true
{} instanceof Object // true
new Date() instanceof Date // true
"name" in user // true if user has a "name" property
0 in [10, 20] // true (index 0 exists)
Bitwise Operators (Less Common)
Operate on 32-bit integers. You’ll rarely touch these unless you’re writing low-level code, parsers, or graphics.
| Op | Name | Example |
|---|---|---|
& |
AND | 5 & 3 → 1 |
\| |
OR | 5 \| 3 → 7 |
^ |
XOR | 5 ^ 3 → 6 |
~ |
NOT | ~5 → -6 |
<< |
Left shift | 5 << 1 → 10 |
>> |
Sign-propagating right shift | -5 >> 1 → -3 |
>>> |
Zero-fill right shift | -5 >>> 1 → 2147483645 |
const FLAG_READ = 0b001;
const FLAG_WRITE = 0b010;
const FLAG_ADMIN = 0b100;
const perms = FLAG_READ | FLAG_WRITE; // 0b011
(perms & FLAG_READ) !== 0 // true
(perms & FLAG_ADMIN) !== 0 // false
Warning: Don’t use bitwise operators as boolean shortcuts (a & b vs a && b). They behave differently and read confusingly.
Operator Precedence
When you mix operators, precedence decides what runs first. Higher number = runs first. Same level → check associativity (left-to-right or right-to-left).
| Precedence | Operator | Description |
|---|---|---|
| 18 | () |
Grouping |
| 17 | . ?. [] () new |
Member access, function call |
| 14 | ++ -- ! ~ + - typeof void delete await (unary) |
Unary |
| 13 | ** |
Exponentiation (right-assoc) |
| 12 | * / % |
Multiplicative |
| 11 | + - |
Additive |
| 10 | << >> >>> |
Bitwise shift |
| 9 | < <= > >= in instanceof |
Relational |
| 8 | == != === !== |
Equality |
| 7 | & |
Bitwise AND |
| 6 | ^ |
Bitwise XOR |
| 5 | \| |
Bitwise OR |
| 4 | && |
Logical AND |
| 3 | \|\| ?? |
Logical OR / Nullish |
| 2 | ? : |
Ternary |
| 1 | = += -= etc. |
Assignment (right-assoc) |
| 0 | , |
Comma |
1 + 2 * 3 // 7 — * before +
(1 + 2) * 3 // 9 — () forces order
2 ** 3 ** 2 // 512 — right-assoc: 2 ** (3 ** 2) = 2 ** 9
a = b = c = 5 // right-assoc: a = (b = (c = 5))
Tip: When in doubt, add parentheses. They cost nothing at runtime, make intent obvious, and disarm subtle bugs.
The Comma Operator
, evaluates left to right and returns the last value. Mostly used in for loops with multiple counters; avoid elsewhere.
let x = (1, 2, 3);
console.log(x); // 3
for (let i = 0, j = 10; i < j; i++, j--) {
// both i and j change each iteration
}
?? and || Can’t Mix Without ()
A small but important rule:
a || b ?? c // ❌ SyntaxError
(a || b) ?? c // ✅
a || (b ?? c) // ✅
The spec forces parentheses to avoid ambiguity. Take the hint and clarify intent.
A Realistic Example
function buildOrderSummary(order, user) {
// Optional chaining + nullish coalescing
const customerName = user?.profile?.name ?? "Guest";
// Ternary
const tier = order.total >= 100 ? "Priority" : "Standard";
// Logical AND for guards
const isExpedited = order.shipping?.method === "express" && order.paid;
// Short-circuit defaults
const tax = (order.total ?? 0) * 0.08;
return {
customerName,
tier,
isExpedited,
grandTotal: order.total + tax,
};
}
?. and ?? together replace what used to be 5 lines of defensive if checks.
Best Practices Checklist
- ✅ Always use
===and!== - ✅ Use
??for null/undefined defaults; use||only when you also want to catch0,"",false - ✅ Use
?.for safe property access on possibly-missing objects - ✅ Use
()to make precedence explicit when mixing operators - ✅ Keep ternaries to one level — switch to
if/elsefor anything nested - ❌ Don’t use
==(exceptx == null) - ❌ Don’t use bitwise operators as boolean operators
- ❌ Don’t rely on operator precedence beyond the obvious cases
What’s Next?
We’ll zoom into one operator family — arithmetic — and the gotchas of floating-point math, modulo with negatives, and the Math object’s helpers.