In JavaScript, what value do the `||` and `&&` operators produce — a boolean, or one of their operands? Walk through `'' || 'default'` and `0 && compute()`.
answer
- the test is boolean, the result is not
- one of the two operands comes back verbatim
- first truthy for one, first falsy for the other
- the losing side is never evaluated
- wrap in Boolean() when a real boolean is the contract
basics
~20 sBoth return one of their operands unchanged, never a coerced boolean. || returns the left operand if it is truthy, otherwise the right one; && returns the left operand if it is falsy, otherwise the right one. The unselected side is never evaluated.
solid answer
~50 s`||` and `&&` are selection operators, not boolean operators. Each evaluates its left operand, applies ToBoolean to *test* it, and then returns one of the two operands **as-is** — the test result is thrown away. So `'' || 'default'` is the string `'default'` (the empty string is falsy, so the right operand is returned), and `'a' || 'b'` is `'a'`, not `true`. `&&` mirrors it: `0 && compute()` short-circuits, returns the number `0`, and `compute()` is never called at all; `'a' && 42` returns `42`. Two consequences matter in practice. First, the result can be any type, so `x && x.length` yields `0`, `undefined` or a number depending on `x`. Second, short-circuiting means the right operand's side effects are conditional — which is exactly why `obj && obj.load()` is safe. If you need an actual boolean, wrap the whole thing: `Boolean(a || b)` or `!!(a || b)`.
code
javascript · 11 lineslet calls = 0;
const compute = () => { calls++; return 99; };
console.log('' || 'default'); // 'default' (right operand returned)
console.log('a' || 'b'); // 'a' (left operand, not true)
console.log(0 && compute()); // 0 (left operand, not false)
console.log('a' && compute()); // 99
console.log(calls); // 1 — the short-circuited call never ran
console.log(typeof ('a' || 'b')); // 'string'
console.log(typeof Boolean('a' || 'b')); // 'boolean'go deeper
Know that these operators hand back one of the two values you gave them rather than true or false, and be able to say what '' || 'default' evaluates to.
Explain the four-step evaluation rule, that ToBoolean is applied only to the left operand as a test, and that the unselected operand is never evaluated at all.
Show that you police return contracts: an expression like list && list.length escaping a function as its return value is a type leak you would catch in review and wrap in Boolean.
Own the readability tradeoff — short-circuit expressions used as statements hide conditional side effects from reviewers, so set a house rule on where the operand-returning idiom is welcome and where an explicit if belongs.
## They are not boolean operators The name "logical operators" is misleading. In many languages `||` and `&&` take booleans and produce booleans. In JavaScript they take *any two values* and produce **one of those two values**, unchanged. The boolean only appears internally, as the test. The evaluation rule for `a || b`: 1. Evaluate `a`. 2. Apply ToBoolean to the result. 3. If it is `true`, return `a`'s value — `b` is never evaluated. 4. Otherwise evaluate `b` and return its value, whatever it is. `a && b` is the exact mirror: 1. Evaluate `a`. 2. Apply ToBoolean to the result. 3. If it is `false`, return `a`'s value — `b` is never evaluated. 4. Otherwise evaluate `b` and return its value. A useful one-liner: `||` returns the **first truthy operand, or the last operand**; `&&` returns the **first falsy operand, or the last operand**. Chains follow directly — `a || b || c` yields the first truthy of the three, falling back to `c`. ```js '' || 'default'; // 'default' — '' is falsy, so the right operand is returned 'a' || 'b'; // 'a' — not true 0 && compute(); // 0 — compute() is never called 'a' && 42; // 42 null && anything; // null — the falsy operand itself comes back ``` Notice that `0 && compute()` returns the number `0`, not `false`. The falsy operand is handed back **verbatim**, preserving its type. This is the detail interviewers are actually probing. ## Short-circuiting is a control-flow feature Because the unselected operand is never evaluated, `&&` and `||` are the only operators besides `?:` and `??` that can skip work. This underpins three everyday idioms: ```js // guarded call — load() runs only if obj is truthy obj && obj.load(); // cheap-first ordering — the expensive check runs only when needed if (cache.has(key) || expensiveLookup(key)) { /* … */ } // fallback value const name = input || 'anonymous'; ``` It also means an operand with side effects is conditional, which cuts both ways: `count++ && log()` increments always, while `flag && count++` increments only sometimes. When reviewing, treat any function call on the right of `&&`/`||` as "may not happen". ## Returning an operand, not a boolean, has consequences Because the result carries its original type, a value can leak somewhere you expected a boolean: ```js function hasItems(list) { return list && list.length; // returns undefined, 0, or a number — not a boolean } ``` The caller writing `if (hasItems(x))` still works, because they re-apply ToBoolean — but the function's contract is a lie, `hasItems(x) === true` fails, and serialising the result puts a number in your JSON. If a boolean is the contract, produce one: `return Boolean(list && list.length)` or `return !!list?.length`. The contrast with `!` is worth stating explicitly: `!` always yields a genuine boolean, so `!!x` is the canonical coercion. `x || y` is not a coercion at all. ## Precedence and grouping `&&` binds tighter than `||`, so `a || b && c` parses as `a || (b && c)`. Both bind looser than comparison operators, which is why `a === 1 || a === 2` works without parentheses. Mixing either operator with `??` in the same expression without parentheses is a **SyntaxError** — the language forces you to be explicit rather than guess a precedence. ## Assignment forms `||=` and `&&=` apply the same test and, importantly, **short-circuit the assignment itself**: `a ||= b` assigns only when `a` is falsy, so a setter or a proxy trap does not fire on the skipped path. That is a different behaviour from `a = a || b`, which always writes. ## What a strong answer sounds like Say the mechanism, not the folklore: the operator tests the left operand with ToBoolean and returns one of the operands unchanged, so the result's type is whatever was selected; the other operand is not evaluated; and if you need a real boolean you wrap the expression in `Boolean(...)` or `!!`. Then give one concrete pair — `0 || 'x'` is `'x'`, `0 && f()` is `0` and `f` never runs — because that pair demonstrates both halves at once.
- How would you turn `list && list.length` into something a caller can safely compare with `=== true`?Wrap the whole expression so ToBoolean runs once at the end: `Boolean(list && list.length)` or `!!(list && list.length)`. Both produce a real boolean regardless of which operand was selected. Returning the raw expression leaks `undefined` or `0` into callers, breaks strict comparison, and puts a number into anything that serialises the result.
- Does `a ||= b` behave identically to `a = a || b`?Not quite. Both assign only when `a` is falsy in terms of the resulting value, but `a = a || b` performs the assignment unconditionally — it writes `a` back to itself on the truthy path. `||=` short-circuits the write itself, so a setter, a proxy `set` trap, or a reactive property is not triggered when the left side is already truthy.
- Why does `a ?? b || c` fail to parse in JavaScript?It is a deliberate SyntaxError. `??` and the logical operators have confusable precedence and different tests — one checks nullishness, the other truthiness — so the grammar forbids mixing them unparenthesised rather than silently picking an order. Write `(a ?? b) || c` or `a ?? (b || c)` to state which you mean.
saying these in an interview costs you the question
- Says `'a' || 'b'` evaluates to true
- Thinks `0 && f()` returns false rather than 0
- Believes both operands are always evaluated
- Claims `||` coerces its result to a boolean
- Confuses `a ||= b` with an unconditional assignment