Topic 2.2
Logical Operators
In one line
&&, || and ! combine true/false conditions, but && and || really return one of their operands and stop early (short-circuit); ?? picks a fallback only for null or undefined, and ||=, &&= and ??= assign on the same rules.
Think of it like this
Getting into a cinema. You need a ticket and to be on time: if you have no ticket, nobody checks your watch. You can pay by cash or card: if you have cash, nobody asks for your card. And a sign saying "seat: not assigned yet, use any free seat" is like a fallback that only applies when there is no seat number at all, not when your seat is number 0 in the front row.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Logical operator
- An operator that combines or flips conditions:
&&(and),||(or),!(not). - Truthy / falsy
- Whether a value counts as true or false in a condition. The falsy ones are
false,0,-0,0n,"",null,undefinedandNaN; everything else is truthy. - Short-circuit
- Stopping early: the right side of
&&,||or??isn't run when the left side already decides the answer. - Nullish
- Exactly
nullorundefined, the two "no value" values. - Nullish coalescing (??)
- An operator that gives a fallback only when the left side is
nullorundefined. - Default value
- The value you use when the real one is missing.
- Logical assignment
||=,&&=and??=: assign only when the left side is falsy, truthy or nullish.
Step by step
01Combine conditions
Most of the time you use logical operators inside an if: "logged in and has paid", "is admin or is owner", "not empty".
const loggedIn = true;
const hasPaid = false;
console.log(loggedIn && hasPaid); // false
console.log(loggedIn || hasPaid); // true
console.log(!hasPaid); // true02Notice they return an operand
|| gives back the first truthy value it finds (or the last value if none is truthy). && gives back the first falsy value (or the last value if all are truthy). Read them as "find the first value that decides".
03Use short-circuiting as a guard
Because the right side is skipped, && can protect it: cart && cart.items.length won't crash when cart is null. || and ?? can provide a fallback: name || "Guest".
04Pick || or ?? for defaults
Ask: "is 0, "" or false a real answer here?" If yes, use ??, which only replaces null and undefined. If no, || is fine.
const volume = 0; // the user muted it
console.log(volume || 50); // 50 (wrong: un-muted!)
console.log(volume ?? 50); // 0 (right)05Fill in missing values with ??=
Logical assignment updates a variable or property in place, only when needed. It's the shortest correct way to set defaults on an options object.
const options = { retries: 0 };
options.retries ??= 3; // stays 0
options.timeout ??= 5000; // added: was undefinedTry it yourself
- 1
Predict the operand
In the first example, add
console.log("cat" && "dog", "cat" || "dog");. Predict both before running. (dog cat.) - 2
Make the guard fail
In the second example, change
const cart = null;toconst cart = { items: [1, 2] };. What does the last line print now? (2.) - 3
Swap the operators
In the third example, change the page's volume line to use
||instead of??. The page now says 50 even though the user muted it. Switch it back.
Code & diagrams
`||` returns the first truthy operand, `&&` the first falsy one (or the last operand if none decides). Only `!` always returns a boolean.
Console
(nothing was printed)Expected console output
false true false
Asha
Guest
0
yes
null
found
true false true falseWithout the `cart &&` guard, `cart.items` would throw a TypeError. `?.` (ES2020) does the same job more neatly: `cart?.items.length`.
Console
(nothing was printed)Expected console output
checked a
checked c
checked e
checked f
nullA volume of `0` is a real setting, so `??` is right. For the display name, an empty string should fall back, so `||` is right there.
Preview
Console
Nothing logged yet.Expected console output
volume || 50
volume ?? 0
name || 'Anonymous'
name ?? ''
theme ?? light
missing ?? 16Console
(nothing was printed)Expected console output
{ retries: 3, label: '', user: 'guest', timeout: 5000 }
ABC 0Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Mix ?? with || without parentheses
In the third example, add console.log(null || undefined ?? "x");.
Break #2
Use || where 0 is a valid value
In the third example, set volume: 0 and display it with settings.volume || 50.
Myth vs fact
Myth
&& and || always return true or false.
Fact
They return one of their operands unchanged. Only ! (and Boolean()) always produce a boolean.
Myth
?? and || are interchangeable.
Fact
|| replaces every falsy value (0, "", false, NaN...). ?? replaces only null and undefined. For defaults where 0 or "" are valid, only ?? is correct.
Myth
a ||= b is exactly a = a || b.
Fact
When a is already truthy, ||= doesn't assign at all, so no setter runs and assigning to a const holding a truthy value doesn't throw. a = a || b always assigns.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
In the spec,
&&,||and??are not operators that evaluate both sides; they are control flow. That's why they can't be overloaded or called as functions, and why minifiers turnif (a) b();intoa && b();. Prefer theifin source code: it's clearer and the minifier will do the rest. - ▸
??was designed with a deliberate SyntaxError when mixed with&&/||, so that the meaning ofa ?? b || cnever depends on a precedence rule nobody remembers. TypeScript and linters enforce the same. - ▸
React and other JSX code often write
{count && <List />}. Whencountis0,&&returns0and React renders the digit 0. Use{count > 0 && <List />}instead: a classic bug caused by&&returning an operand (the React course at/reactcovers it in Topic 1.4). - ▸
Short-circuiting is also used for lazy evaluation of expensive work:
cache.get(key) ?? compute(key)only computes on a miss. That's memoisation in miniature (Topic 15.3).
Remember this
- 1
**
&&(AND) means "both".||(OR) means "at least one".!(NOT)** flips a value:!trueisfalse. With real booleans they work like a truth table:true && falseisfalse,true || falseistrue.!always returns a boolean, after converting its operand by the truthy/falsy rules (Topic 1.6):!0istrue,!"hi"isfalse. Two of them,!!value, is a common way to turn any value into its boolean, the same asBoolean(value). - 2
Here is what most tutorials skip:
&&and||don't returntrueorfalse. They return one of their operands, unchanged.a || breturnsaifais truthy, otherwiseb.a && breturnsaifais falsy, otherwiseb. So"Asha" || "Guest"is"Asha","" || "Guest"is"Guest", and0 && "yes"is0. In anifthis makes no difference (the result is checked for truthiness anyway), but when you store the result it matters a lot. - 3
Short-circuiting means the right side is only evaluated when it's needed. In
a && b, ifais falsy the answer is already known, sobnever runs. Ina || b, ifais truthy,bnever runs. Programs use this on purpose:user && user.namereadsnameonly if there is a user, andisReady || start()callsstart()only if not ready. Any function call or assignment on the skipped side simply doesn't happen. - 4
**
??(nullish coalescing,since ES2020) returns the right side only when the left side is nullish**:nullorundefined. Unlike||, it keeps every other falsy value:0 ?? 50is0,"" ?? "none"is"",false ?? trueisfalse. Use??for defaults whenever0, an empty string orfalseare valid answers (a volume of 0, an empty search, a setting turned off). Use||when any falsy value should be replaced.??short-circuits too. - 5
**Logical assignment (
since ES2021)** combines these with=.a ||= bassignsbonly ifais falsy;a &&= bassigns only ifais truthy;a ??= bassigns only ifais nullish. They are not quitea = a || b: when no assignment is needed, nothing is written at all, which matters for setters, read-only properties andconstvariables.options.retries ??= 3is the idiomatic way to fill in a missing setting. - 6
Precedence:
!runs before everything else here, then&&, then||. Soa || b && cmeansa || (b && c).??has the same low level as||, but the language forbids mixing??with&&or||without parentheses:a || b ?? cis a SyntaxError. Write(a || b) ?? cto say what you mean. A related ES2020 operator, optional chaining?., stops a property read atnull/undefinedand pairs naturally with??:user?.name ?? "Guest"(Topic 6.2 covers it in depth).
Explain it without notes
What do && and || actually return?
What is short-circuit evaluation, and how is it useful?
When should you use ?? instead of ||?
What do ||=, &&= and ??= do?
Practice
Given const input = "";, print the input or the text (empty) if it's empty.
Given const page = { size: 0 };, set size to 20 only if it is missing, and add sort: "name" if sort is missing. Print the object.
Predict, then check: console.log(null ?? 0 || 5) — why doesn't it run?
Trade-offs
- ↔
a && doThing()is short, but anifstatement is easier to read and to set a breakpoint on; keep short-circuit tricks for expressions, not as a replacement forif. - ↔
??is precise but newer (ES2020): every current browser supports it, but very old targets need a transpiler.
Done when you can
I can predict what
&&,||and!return for any values.I can use short-circuiting to guard property access and to give fallbacks.
I can choose between
||and??for a default value.I can use
||=,&&=and??=and explain when they assign.I know
??can't be mixed with&&/||without parentheses.