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.
Loading visual…
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 skippedThe 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 evaluatedThe 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.
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.