?? vs ||. || returns the left operand if it is truthy, otherwise the right. ?? returns the left operand unless it is null or undefined. So 0, '', false, and NaN survive ?? but not ||. Use ?? for "was a value provided?" defaults, and || only when every falsy value should be replaced. Both short-circuit: the right side isn't evaluated if the left decides the result. Mixing ?? with || or && without parentheses is a SyntaxError, because the grammar refuses to guess the precedence.
Optional chaining ?.. In a?.b, a?.[k], and a?.(), if the base is null or undefined the whole chain evaluates to undefined. Key points:
- It short-circuits everything to its right in the same chain:
a?.b.c.d()never touches.cor.dwhenais nullish — but(a?.b).cdoes, because the parentheses end the chain. - It only guards the exact spot where it's written.
a?.b.cstill throws ifaexists anda.bisundefined. - It checks nullishness, not existence or type:
a.fn?.()throwsTypeErroriffnis a non-function like5. - The key expression of
?.[expr]isn't evaluated when short-circuited, anda.m?.()keepsthisasa. - It can't be an assignment target (
a?.b = 1is aSyntaxError), butdelete a?.bis allowed.
Logical assignment. a ||= b, a &&= b, a ??= b mean "evaluate a; if the condition holds, assign". They aren't a = a || b: when the condition fails, no assignment happens at all (no setter call, no write to a frozen property), and b isn't evaluated. ||= assigns if a is falsy, &&= if truthy, ??= if nullish.