Expressions & Operators
An expression resolves to a value (3 + 4, x = 7); a statement is a complete instruction. Any expression used on its own becomes an expression statement. Operators combine operands; precedence decides grouping, associativity breaks ties. Use () to override: (1 + 2) * 3 → 9 vs 1 + 2 * 3 → 7.
Assignment
= returns the assigned value, which is why chaining works — and why it evaluates left-to-right but groups right-to-left:
y = x = f(); // f() runs once → assigned to x → then to y
y = [f(), x = g()]; // logs "F!" then "G!"; y = [2, 3], x = 3
Avoid const z = y = x = f() — only z is declared; x/y leak as globals in sloppy mode.
Compound forms: += -= *= /= %= **=, bitwise <<= >>= >>>= &= ^= |=, and the logical ones:
x &&= f(); // x && (x = f()) — assign only if x truthy
x ||= f(); // x || (x = f()) — assign only if x falsy
x ??= f(); // x ?? (x = f()) — assign only if x null/undefined
Destructuring unpacks into targets: const [a, b] = arr; and const { x, y } = obj;.
Gotcha — assigning a property to a primitive fails silently (throws in strict mode):
const val = 0;
val.x = 3;
val.x; // undefined
Comparison
| coercion | 3 == "3" | |
|---|---|---|
== / != | yes | true |
=== / !== | no | false |
Prefer ===. Relational < <= > >= compare numerically for numbers, lexicographically (Unicode) for strings — so "2" < "12" is false but 2 < 12 is true.
Arithmetic
+ - * / % (remainder), ** (exponentiation, right-associative: 2 ** 3 ** 2 = 512). 1 / 0 → Infinity (not an error). ++/-- differ by position: prefix returns the new value, postfix returns the old.
Bitwise
Operands coerced to 32-bit signed ints. Logical & | ^ ~; ~x === -(x + 1). Shifts: << (left), >> (sign-propagating right), >>> (zero-fill right — no BigInt equivalent).
Logical & short-circuit
Each returns an operand, not a boolean, and stops early:
a && b // first falsy, else last ("Cat" && "Dog" → "Dog")
a || b // first truthy, else last (false || "Cat" → "Cat")
a ?? b // a unless a is null/undefined
?? beats || for defaults because it treats 0, "", false as valid (only nullish triggers the fallback). ! coerces to boolean and inverts.
BigInt
Most operators work, but: no >>>, and you cannot mix BigInt with Number — 1n + 2 throws. Convert explicitly (Number(1n) / BigInt(2)). Division truncates: 1n / 2n → 0n. Mixed comparison is fine: 3 > 2n → true.
Other operators
- Ternary:
cond ? a : b— the only three-operand operator. - Comma: evaluates both, returns the last; legit use is
for (i=0, j=9; …; i++, j--). - String:
+concatenates;+=appends. - Unary:
+x/-x(numeric convert / negate),!(boolean invert),typeof,void expr(→undefined),delete obj.prop(returnsfalseon non-configurable; on arrays leaves a hole,lengthunchanged). - Relational:
in(property exists, including inherited —"length" in []→true),instanceof(walks the prototype chain).
typeof quirks
typeof null // "object" ← historic bug
typeof [] // "object"
typeof function(){}// "function"
typeof undeclared // "undefined" (safe — no ReferenceError)
Left-hand-side expressions
new Ctor(args)— instantiate.super(...)/super.method()— parent constructor/methods.- Spread
...— expand iterables/objects in calls and literals. - Property access:
obj.x,obj["x"],obj[key]. - Optional chaining
?.— short-circuits toundefinedif the left side is null/undefined:a?.b,a?.[k],fn?.().
Precedence (high → low)
() access/call → ** → unary (! ~ + - ++ -- typeof void delete) → * / % → + - → shifts → relational (< <= > >= in instanceof) → equality → & → ^ → | → && → || → ?? → ?: → assignment → ,