JavaScript 06 · JavaScript in general
Conditions and logical operators
Truthy and falsy, the ternary, &&, ||, ??, ?., switch and early return.
Loading lesson…
JavaScript 06 · JavaScript in general
Truthy and falsy, the ternary, &&, ||, ??, ?., switch and early return.
Loading lesson…
A program runs like a train on rails – line after line. Conditions are the switches: depending on a question, they send the train one way or the other. "Is the user signed in?" "Is the cart empty?" "Has the data arrived from the server?"
JavaScript has two groups of tools for switches. Statements (if, switch) control which lines run. Operators (? :, &&, ||, ??, ?.) pick a value right inside an expression – and those are the ones you'll meet most in React, because you can't write an if inside JSX.
const age = 17if (age >= 18) {console.log('Adult')} else if (age >= 13) {console.log('Teenager')} else {console.log('Child')}// The one-line form – without braces only for a single statementif (age < 0) return
When a function has to check several things, don't nest the conditions inside each other. Deal with the exceptions first and stop, and leave the main case for the end:
// ❌ A pyramidfunction checkout(cart, user) {if (user) {if (cart.length > 0) {if (user.address) {return sendOrder(cart, user)} else {return 'The address is missing'}} else {return 'The cart is empty'}} else {return 'Please sign in'}}// ✅ Early return – reads top to bottom like a checklistfunction checkout(cart, user) {if (!user) return 'Please sign in'if (cart.length === 0) return 'The cart is empty'if (!user.address) return 'The address is missing'return sendOrder(cart, user)}
When JavaScript needs a yes/no (in an if, a ternary, with &&) and gets something other than a boolean, it converts it. There are only six false (falsy) values – everything else is true (truthy):
| Falsy – counts as NO | Truthy – counts as YES (even when it doesn't look like it) |
|---|---|
false | true |
0 | 42, -1, 0.5 |
'' (empty text) | '0', 'false', ' ' (a space) |
null | [] (an empty array) |
undefined | {} (an empty object) |
NaN | every function, every date… |
if ('hello') { … } // runsif ('') { … } // doesn't runif (0) { … } // doesn't runif ([]) { … } // ⚠️ runs – an empty array is truthy!if (items.length > 0) // ✅ the right question "does the array contain anything?"if (items.length) // works too (0 is falsy), but it's less clear what you're askingif (Object.keys(obj).length > 0) // "does the object have any properties?"Boolean('hello') // true – conversion to a boolean!!'hello' // true – the same as a shortcut (double negation)
const age = 25const hasTicket = trueage >= 18 && hasTicket // true – AND: both must holdage < 18 || hasTicket // true – OR: one is enough!hasTicket // false – NOT: flips the value// Combinations – parentheses for safety and readabilityconst canEnter = (age >= 18 && hasTicket) || isVipconst isWorkday = day !== 'saturday' && day !== 'sunday'const isWeekend = ['saturday', 'sunday'].includes(day) // often easier to read
A ternary is an if/else squeezed into an expression: condition ? valueIfYes : valueIfNo. Unlike if, it has a value, so you can store it in a variable or put it anywhere in an expression.
const label = isDone ? 'Done' : 'To do'const price = isMember ? basePrice * 0.9 : basePriceconst greeting = `Good ${hour < 12 ? 'morning' : 'afternoon'}`const className = `btn ${isActive ? 'btn-active' : ''}`// The same with if – longer and needs letlet labelif (isDone) {label = 'Done'} else {label = 'To do'}
Here's the catch: in JavaScript, && and || don't return true/false. They return one of their sides:
| Expression | When a is truthy | When a is falsy |
|---|---|---|
a && b | returns b | returns a |
a || b | returns a | returns b |
'hello' && 'world' // 'world''' && 'world' // ''0 && 'world' // 0 ← careful, the number 0!null && 'world' // null'hello' || 'default' // 'hello''' || 'default' // 'default'0 || 'default' // 'default' ← the zero "got lost"// Typical useuser && user.name // read name only if user exists (today rather user?.name)const name = input || 'Anonymous' // a default value for an empty inputisLoggedIn && showWelcome() // call it only when the condition holds
a ?? b returns b only when a is null or undefined. Zero, empty text and false are valid values for it.
const settings = { volume: 0, nickname: '', notifications: false }settings.volume || 50 // 50 ❌ the user muted it to 0 and we turned it up to 50settings.volume ?? 50 // 0 ✅settings.notifications || true // true ❌ the user turned them offsettings.notifications ?? true // false ✅settings.theme ?? 'light' // 'light' – the property is missing → the default value// Assignment with a default valueoptions.retries ??= 3 // set it to 3 only if there's nothing there
| Value a | a || "x" | a ?? "x" |
|---|---|---|
0 | "x" | 0 |
'' | "x" | '' |
false | "x" | false |
null | "x" | "x" |
undefined | "x" | "x" |
'hello' | 'hello' | 'hello' |
const user = { name: 'Anna', address: null }user.address.city // 💥 TypeError: Cannot read properties of nulluser.address?.city // undefined – ?. stops and returns undefineduser.contacts?.[0] // undefined – works with square brackets toouser.getAge?.() // undefined – and with calling a function that may not exist// The typical pair: read safely + a default valueconst city = user.address?.city ?? 'unknown'// Data from the server that hasn't arrived yetconst firstTitle = response?.data?.items?.[0]?.title ?? 'No items'
What you see: A row of values for x and, below it, what converting to a boolean, a ternary, &&, || and ?? make of each one.
Try it:
x && 'right' returns 0 (not false!), || replaces the zero, ?? keeps it.|| and ?? differ, for null they don't.The takeaway: There are only six falsy values. && and || return one of the sides, not a boolean. ?? only reacts to null and undefined.
Loading the interactive part…
When you compare one value with many options, switch is clearer than a chain of ifs:
function statusLabel(status) {switch (status) {case 'new':return 'New order'case 'paid':return 'Paid'case 'shipped':case 'delivered': // several cases with the same resultreturn 'On its way'default:return 'Unknown status'}}
In functions that just turn one value into another, a dictionary (an object) is often even shorter:
const STATUS_LABELS = { new: 'New order', paid: 'Paid', shipped: 'On its way' }const statusLabel = (status) => STATUS_LABELS[status] ?? 'Unknown status'const ICONS = { success: '✅', error: '❌', warning: '⚠️' }const icon = ICONS[type] ?? 'ℹ️'
You'll see switch, on the other hand, in React reducers (lesson React 05), where every action does something different, not just returns a value.
Two short functions that make decisions: by age, and by whether we have a name at all.
Task is in the comments in the code below – at the top of the file and at the places marked TODO.
Loading the interactive part…
Six functions that make decisions and safely read data that may be missing. Watch out for the difference between || and ?? – it matters for a volume of 0 and an empty nickname. What exactly each function should return is in the comments in the code.
Medium and hard challenges unlock once you sign in. Like the whole course, they are free – just an e-mail, no password and no payment.
false, 0, '', null, undefined and NaN. An empty array and an empty object are truthy.a ? b : c is an if/else that returns a value. Don't nest more than one.&& and || return one of the sides, not a boolean – 0 && x is 0.?? fills in a default value only for null/undefined, || also for 0 and ''. You almost always want ??.?. safely reads from a value that may be missing. Typically a?.b ?? default.