← All concepts
Data Structures, Operators & Strings

Short-Circuit Evaluation and the Ternary Operator

&&, ||, and ?? can skip evaluating their right side entirely, and the ternary operator picks exactly one of two values from a single condition.

Definition

`&&` and `||` do not always evaluate both sides. `&&` checks its left operand first: if it is falsy, `&&` returns that value immediately and never evaluates the right side at all; only if the left side is truthy does it evaluate and return the right side. `||` works the opposite way: if the left operand is truthy, it returns that value right away and skips the right side; only when the left side is falsy does it evaluate and return the right side. This is called short-circuiting, and it means the skipped side's expression, including any function call it contains, never runs. `??` (nullish coalescing) looks similar to `||` but checks a narrower condition: it only falls through to its right side when the left side is `null` or `undefined`, not for any other falsy value. This matters a lot for numbers like `0` or an empty string `""`, which are falsy but often valid, real values: `0 || 5` gives `5` because `0` is falsy, while `0 ?? 5` gives `0` because `0` is neither null nor undefined. The ternary (conditional) operator, written `condition ? ifTrue : ifFalse`, is a separate but related idea: it evaluates the condition once and then evaluates and returns only the branch that matches, so exactly one of `ifTrue`/`ifFalse` ever runs.

Examples

const user = null;
user && user.name; // null, user.name is never read

const count = 0;
count || 10; // 10, because 0 is falsy
count ?? 10; // 0, because 0 is not null or undefined

&& stops at the falsy user and never touches user.name; || treats 0 as "missing" and replaces it, but ?? correctly keeps 0 since it only checks for null/undefined.

function log(label, value) {
  console.log(label, value);
  return value;
}
true || log("never runs", 1);
false && log("never runs", 2);
// neither log() call executes; both calls are skipped

The right side of || is skipped once the left side is truthy, and the right side of && is skipped once the left side is falsy, so log() is never called in either line.

const age = 20;
const label = age >= 18 ? "adult" : "minor";
// "adult"; the "minor" branch is never evaluated

The ternary evaluates the condition once, then evaluates and returns only the matching branch, here "adult", leaving the other branch untouched.

Common mistakes

  • Reaching for || to supply a default and accidentally replacing valid falsy values like 0 or ""; ?? avoids this by checking only for null/undefined.
  • Assuming the right side of && or || always runs; it may be skipped entirely, so it should not be relied on for a required side effect.
  • Chaining several ternaries into one dense, hard-to-read line instead of a clearer if/else or a small lookup.

Key takeaways

  • && returns the first falsy operand it finds, or the last operand if every operand is truthy.
  • || returns the first truthy operand it finds, or the last operand if every operand is falsy.
  • ?? only falls through on null or undefined, so 0, "", and false pass through it untouched.
  • The ternary operator evaluates exactly one of its two branches based on a single condition.
Check your understanding

const count = 0; const a = count || 10; const b = count ?? 10; What are a and b?

Where you see this

  • Providing a default value with ?? that still allows 0, an empty string, or false as a real, intentional value.
  • Guarding a property access with && before a chain existed, like user && user.profile && user.profile.bio.
  • Choosing between two small render values or labels with a ternary instead of a full if/else block.
  • Skipping an expensive or side-effecting call on the right of && or || when the left side already decides the outcome.

Practice this

Further reading

Previous
Template Literals and Strings
Next
Object.keys, Object.values, and Object.entries