JavaScript Operators

7 minute read

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 catch 0, "", 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/else for anything nested
  • ❌ Don’t use == (except x == 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.

← Previous: JS Data Types Next: JS Arithmetic →

Updated: