AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 08 — Operators — Precedence, Short-Circuiting, and the Bitwise Oddities

08 — Operators — Precedence, Short-Circuiting, and the Bitwise Oddities

August 13, 20265 min read
Download as Markdown

"Plus, minus, and equals" was my operator inventory, and it skipped the two ideas that actually matter. The idea that everything else hangs off: operators are just functions with weird syntax, and the two things that actually matter are precedence (which operator binds tighter) and the fact that && / || return one of their operands, not a strict boolean. [1]

The framing that finally landed is grouping operators by what they do, then learning the two cross-cutting rules — precedence and short-circuiting — that govern all of them.

The families

JavaScript has a handful of operator families, each with predictable members [1][2]:

  • Arithmetic — +, -, *, /, % (remainder), ** (exponentiation). The + overload is the famous one: with a string operand it concatenates, otherwise it adds.
  • Comparison — >, <, >=, <=, ==, ===, !=, !==. The strict variants (===, !==) never coerce; the loose ones do.
  • Logical — && (AND), || (OR), ! (NOT). The trap: && and || don't return true/false, they return one of their operands.
  • Assignment — =, plus compound forms +=, -=, *=, ||=, &&=, ??=. The compound forms combine an operation with assignment.
  • Bitwise — &, |, ^, ~, <<, >>, >>>. Treat operands as 32-bit integers and operate bit-by-bit.
  • Unary — +x (to number), -x (negate), ++/-- (increment/decrement), typeof, !, delete.
  • Ternary — condition ? a : b, the only three-operand operator. A concise if/else for expressions.
  • Comma — , evaluates both operands left-to-right and returns the last. Rare except in for loop headers.

Precedence and associativity

Precedence decides which operator wins when several appear together: 2 + 3 * 4 is 14, not 20, because * binds tighter than + [3]. Associativity decides the order for equal-precedence operators: most are left-to-right (10 - 3 - 2 is 5), but assignment and exponentiation are right-to-left (2 ** 3 ** 2 is 512, not 64).

The practical rule: don't memorize the table — use parentheses. (2 + 3) * 4 is unambiguous and reads the same to the next person. When a line has three operators without parentheses, it's a bug waiting for someone to misread it.

The logical-operator trap

This is the part that took me longest to internalize. && and || in JavaScript do not return booleans — they return one of their two operands, picked by short-circuit evaluation [4]:

  • a && b — evaluates a. If a is falsy, returns a (short-circuits, b never runs). If a is truthy, returns b.
  • a || b — evaluates a. If a is truthy, returns a (short-circuits). If a is falsy, returns b.

That's why the default-value pattern works:

const name = user.name || "Anonymous"; // if user.name is falsy, use "Anonymous"
const value = config?.enabled && config.value; // if enabled is truthy, take config.value

The operands don't have to be booleans. 0 || "fallback" returns "fallback"; "hello" && 42 returns 42. The result of &&/|| is always one of the operands verbatim, not a coerced boolean.

The newer nullish coalescing operator ?? is the stricter cousin of ||: it only falls through on null or undefined, not on every falsy value. 0 ?? "fallback" is 0 (zero is a real value, keep it), where 0 || "fallback" would have replaced it. For default-value logic where 0, "", or false are legitimate inputs, ?? is the correct choice.

The ternary operator

condition ? valueIfTrue : valueIfFalse is an expression, not a statement — it produces a value [5]. That makes it the right tool for conditional assignment and JSX rendering, and the wrong tool for branching side-effects (use if there). Deep nesting (a ? b : c ? d : e) is a readability trap; flatten it with early returns or a lookup table.

Bitwise operators: the niche corner

Bitwise operators treat operands as 32-bit integers and work at the bit level [6]. & (AND), | (OR), ^ (XOR), ~ (NOT), << (left shift), >> (sign-propagating right shift), >>> (zero-fill right shift). They show up in flag manipulation, permission bitmasks, certain algorithm tricks (x >> 1 as a fast Math.floor(x/2) for positives), and some legacy graphics code. For most application code they're a curiosity — useful when needed, but I've gone years without writing one outside of a flags enum.

How I use this

Three habits fall out. First, parenthesize generously — every expression with three or more operators gets parens, no exceptions, because the next reader (including future me) shouldn't have to recall precedence. Second, ?? for defaults over || whenever 0/""/false are valid values — it's the precise tool. Third, ternary for values, if for side-effects — and never nest a ternary deeper than one level. The operator families themselves I rarely think about; the cross-cutting rules (precedence, short-circuit) are what actually cause or prevent bugs.

References

[1] Mozilla, "Expressions and operators," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators

[2] I. Kantor, "Basic operators, maths," The Modern JavaScript Tutorial, 2024. [Online]. Available: https://javascript.info/operators

[3] Mozilla, "Operator precedence," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence

[4] Mozilla, "Logical operators (&&, ||, !)," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#binary_logical_operators

[5] Mozilla, "Conditional (ternary) operator," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#conditional_operator

[6] Mozilla, "Bitwise operators," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#bitwise_operators

Knowledge check · Question 1 of 5

What does `0 || "fallback"` return, and why?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!