Lekce 0.6 · JavaScript obecně
Podmínky a logické operátory
Truthy a falsy, ternár, &&, ||, ??, ?., switch a early return.
Načítám lekci…
Lekce 0.6 · JavaScript obecně
Truthy a falsy, ternár, &&, ||, ??, ?., switch a early return.
Načítám lekci…
Program běží jako vlak po kolejích – řádek za řádkem. Podmínky jsou výhybky: podle nějaké otázky pošlou vlak jednou, nebo druhou cestou. „Je uživatel přihlášený?“ „Je košík prázdný?“ „Došla data ze serveru?“
JavaScript má na výhybky dvě skupiny nástrojů. Příkazy (if, switch) řídí, které řádky se provedou. Operátory (? :, &&, ||, ??, ?.) vybírají hodnotu přímo uvnitř výrazu – a právě ty v Reactu potkáš nejčastěji, protože v JSX se if napsat nedá.
const age = 17if (age >= 18) {console.log('Dospělý')} else if (age >= 15) {console.log('Teenager')} else {console.log('Dítě')}// Jednořádková podoba – bez závorek jen pro jeden příkazif (age < 0) return
Když funkce musí ověřit několik věcí, nevnořuj podmínky do sebe. Nejdřív vyřiď výjimky a skonči, hlavní případ nech na konec:
// ❌ Pyramidafunction checkout(cart, user) {if (user) {if (cart.length > 0) {if (user.address) {return sendOrder(cart, user)} else {return 'Chybí adresa'}} else {return 'Košík je prázdný'}} else {return 'Přihlas se'}}// ✅ Early return – čte se shora dolů jako kontrolní seznamfunction checkout(cart, user) {if (!user) return 'Přihlas se'if (cart.length === 0) return 'Košík je prázdný'if (!user.address) return 'Chybí adresa'return sendOrder(cart, user)}
Když JavaScript potřebuje ano/ne (v if, v ternáru, u &&) a dostane něco jiného než boolean, převede si to. Nepravdivých (falsy) hodnot je jen šest, všechno ostatní je pravdivé (truthy):
| Falsy – bere se jako NE | Truthy – bere se jako ANO (i když to tak nevypadá) |
|---|---|
false | true |
0 | 42, -1, 0.5 |
'' (prázdný text) | '0', 'false', ' ' (mezera) |
null | [] (prázdné pole) |
undefined | {} (prázdný objekt) |
NaN | každá funkce, každé datum… |
if ('ahoj') { … } // provede seif ('') { … } // neprovede seif (0) { … } // neprovede seif ([]) { … } // ⚠️ provede se – prázdné pole je truthy!if (items.length > 0) // ✅ správný dotaz „obsahuje pole něco?“if (items.length) // funguje taky (0 je falsy), ale je méně jasné, na co se ptášif (Object.keys(obj).length > 0) // „má objekt nějaké vlastnosti?“Boolean('ahoj') // true – převod na boolean!!'ahoj' // true – totéž zkratkou (dvojí negace)
const age = 25const hasTicket = trueage >= 18 && hasTicket // true – A ZÁROVEŇ: musí platit obojíage < 18 || hasTicket // true – NEBO: stačí jedno!hasTicket // false – NE: obrátí hodnotu// Kombinace – závorky pro jistotu a čitelnostconst canEnter = (age >= 18 && hasTicket) || isVipconst isWorkday = day !== 'sobota' && day !== 'neděle'const isWeekend = ['sobota', 'neděle'].includes(day) // často čitelnější
Ternár je if/else zmáčknutý do výrazu: podmínka ? hodnotaKdyžAno : hodnotaKdyžNe. Na rozdíl od if má hodnotu, takže ho můžeš uložit do proměnné nebo vložit kamkoli do výrazu.
const label = isDone ? 'Hotovo' : 'Zbývá'const price = isMember ? basePrice * 0.9 : basePriceconst greeting = `Dobr${hour < 12 ? 'é ráno' : 'ý den'}`const className = `btn ${isActive ? 'btn-active' : ''}`// Totéž s if – delší a potřebuje letlet labelif (isDone) {label = 'Hotovo'} else {label = 'Zbývá'}
Tady je zrada: && a || v JavaScriptu nevracejí true/false. Vracejí jednu ze svých stran:
| Výraz | Když je a truthy | Když je a falsy |
|---|---|---|
a && b | vrátí b | vrátí a |
a || b | vrátí a | vrátí b |
'ahoj' && 'svět' // 'svět''' && 'svět' // ''0 && 'svět' // 0 ← pozor, číslo 0!null && 'svět' // null'ahoj' || 'výchozí' // 'ahoj''' || 'výchozí' // 'výchozí'0 || 'výchozí' // 'výchozí' ← nula se „ztratila“// Typické použitíuser && user.name // přečti name, jen když user existuje (dnes spíš user?.name)const name = input || 'Anonym' // výchozí hodnota pro prázdný vstupisLoggedIn && showWelcome() // zavolej, jen když platí podmínka
a ?? b vrátí b, jen když je a null nebo undefined. Nula, prázdný text a false jsou pro něj platné hodnoty.
const settings = { volume: 0, nickname: '', notifications: false }settings.volume || 50 // 50 ❌ uživatel ztlumil na 0 a my mu pustili 50settings.volume ?? 50 // 0 ✅settings.notifications || true // true ❌ uživatel je vypnulsettings.notifications ?? true // false ✅settings.theme ?? 'light' // 'light' – vlastnost chybí → výchozí hodnota// Přiřazení s výchozí hodnotouoptions.retries ??= 3 // nastav na 3, jen když tam nic není
| Hodnota a | a || "x" | a ?? "x" |
|---|---|---|
0 | "x" | 0 |
'' | "x" | '' |
false | "x" | false |
null | "x" | "x" |
undefined | "x" | "x" |
'ahoj' | 'ahoj' | 'ahoj' |
const user = { name: 'Jana', address: null }user.address.city // 💥 TypeError: Cannot read properties of nulluser.address?.city // undefined – ?. se zastaví a vrátí undefineduser.contacts?.[0] // undefined – funguje i s hranatými závorkamiuser.getAge?.() // undefined – a s voláním funkce, která nemusí existovat// Typická dvojice: bezpečně přečíst + výchozí hodnotaconst city = user.address?.city ?? 'neznámé'// Data ze serveru, která ještě nedorazilaconst firstTitle = response?.data?.items?.[0]?.title ?? 'Žádné položky'
Co vidíš: Řada hodnot pro x a pod ní, co s každou udělá převod na boolean, ternár, &&, || a ??.
Vyzkoušej:
x && 'pravá' vrátí 0 (ne false!), || nulu nahradí, ?? ji nechá.|| a ?? liší, u null ne.Co z toho plyne: Falsy je jen šest hodnot. && a || vracejí jednu ze stran, ne boolean. ?? reaguje jen na null a undefined.
Načítám interaktivní část…
Když porovnáváš jednu hodnotu s mnoha možnostmi, je switch přehlednější než řada if:
function statusLabel(status) {switch (status) {case 'new':return 'Nová'case 'paid':return 'Zaplacená'case 'shipped':case 'delivered': // víc případů se stejným výsledkemreturn 'Na cestě'default:return 'Neznámý stav'}}
Ve funkcích, které jen převádějí hodnotu na jinou, je často ještě kratší slovník (objekt):
const STATUS_LABELS = { new: 'Nová', paid: 'Zaplacená', shipped: 'Na cestě' }const statusLabel = (status) => STATUS_LABELS[status] ?? 'Neznámý stav'const ICONS = { success: '✅', error: '❌', warning: '⚠️' }const icon = ICONS[type] ?? 'ℹ️'
switch naopak uvidíš u reducerů v Reactu (lekce 5), kde každá akce dělá něco jiného, ne jen vrací hodnotu.
Jeden ternár a jedno ??.
VÝZVA: Kdo je kdo
1. ageLabel(20) má vrátit 'dospělý', ageLabel(12) 'dítě'. Hranice je 18 let. Použij ternár:
podmínka ? 'dospělý' : 'dítě'
2. nameOrGuest('Jana') má vrátit 'Jana', nameOrGuest(null) 'Host'. Použij ??Načítám interaktivní část…
Šest funkcí, které rozhodují a bezpečně čtou data, která můžou chybět. Pozor na rozdíl mezi || a ?? – testy ho prověří.
Střední a těžké výzvy jsou bonus pro přihlášené. Registrace je zdarma – stačí e-mail, žádné heslo.
false, 0, '', null, undefined a NaN. Prázdné pole i objekt jsou truthy.a ? b : c je if/else, který vrací hodnotu. Nevnořuj víc než jeden.&& a || vracejí jednu ze stran, ne boolean – 0 && x je 0.?? dosadí výchozí hodnotu jen za null/undefined, || i za 0 a ''. Skoro vždy chceš ??.?. bezpečně čte z hodnoty, která může chybět. Typicky a?.b ?? výchozí.Checks, Check)Checks, Check – ze souboru src/course/checks.tsx. Pomocník kurzu pro výzvy s funkcemi: spustí testy a ukáže ✅/❌. Jak funguje uvnitř, nemusíš řešit – stačí, že ti řekne, které kroky už máš hotové./** One test: a description and a function returning true (passed) or false. It may be async. */export type Check = [name: string, test: () => boolean | Promise<boolean>]/*** Runs the tests of a challenge and shows ✅ or ❌ for each.* An error thrown in a test (e.g. when trying to change a frozen object) is shown under the test.*/export function Checks({ tests }: { tests: Check[] }) {const t = useT()const [results, setResults] = useState<Result[]>(() => tests.map(() => ({ status: 'pending' })))useEffect(() => {let ignore = falsetests.forEach(([, test], i) => {Promise.resolve().then(test).then((ok): Result => (ok ? { status: 'ok' } : { status: 'fail' }),(e): Result => ({ status: 'fail', error: e instanceof Error ? e.message : String(e) }),).then((result) => {if (ignore) returnsetResults((prev) => prev.map((r, idx) => (idx === i ? result : r)))})})return () => {ignore = true}}, [tests])const passed = results.filter((r) => r.status === 'ok').lengthreturn (<div className="stack"><strong className={passed === tests.length ? 'ok' : undefined}>{t.checksPassed(passed, tests.length)}</strong><ul className="list">{tests.map(([name], i) => {const r = results[i] ?? { status: 'pending' }return (<li key={name}>{r.status === 'pending' ? '⏳' : r.status === 'ok' ? '✅' : '❌'} {name}{r.status === 'fail' && r.error && (<div className="bad muted">{t.checksError} {r.error}</div>)}</li>)})}</ul></div>)}