Lekce 0.1 · JavaScript obecně
Proměnné, typy a operátory
const a let, čísla a texty, převody typů, === vs. == a užitečné metody řetězců.
Načítám lekci…
Lekce 0.1 · JavaScript obecně
const a let, čísla a texty, převody typů, === vs. == a užitečné metody řetězců.
Načítám lekci…
Když se člověk učí React, často zjistí, že ho nezaskočí React, ale JavaScript kolem něj. React je vlastně jen pár funkcí – všechno ostatní, co v komponentě napíšeš, je obyčejný JavaScript.
Je to jako s vařením podle receptů. Recepty (React) se dají naučit rychle – pokud umíš krájet, smažit a vážit (JavaScript). Když neumíš krájet, je každý recept boj. Prvních osm lekcí je proto takový kurz krájení: obecný JavaScript, bez Reactu. Poslední dvě lekce této části pak ukážou, jak se tyhle dovednosti v Reactu používají.
Proměnná je pojmenovaná přihrádka na hodnotu. V moderním JavaScriptu ji vytvoříš dvěma slovy:
const name = 'Jana' // const = přihrádku už nepůjde přepsatlet count = 0 // let = přihrádku můžeš později naplnit jinou hodnotoucount = count + 1 // ✅ let jde přepsatcount += 1 // ✅ totéž kratšícount++ // ✅ ještě kratší (+1)name = 'Petr' // ❌ TypeError: Assignment to constant variable.
| const | let | var | |
|---|---|---|---|
| Jde přepsat? | ne | ano | ano |
| Platí v | bloku { … } | bloku { … } | celé funkci (matoucí) |
| Používat? | ano, výchozí volba | když hodnotu měníš | ne, je zastaralé |
Proměnná platí jen uvnitř složených závorek { … }, ve kterých vznikla. Venku o ní nikdo neví:
const price = 100if (price > 50) {const discount = 10 // platí jen uvnitř tohohle blokuconsole.log(price - discount) // 90 – vnější proměnné vidíš i uvnitř}console.log(discount) // ❌ ReferenceError: discount is not defined
userName, totalPrice, isLoggedIn.is, has, can, should: isOpen, hasError.users), jedna položka v jednotném (user).getUser, formatPrice, handleClick.Každá hodnota v JavaScriptu má typ. Primitivní typy jsou jednoduché hodnoty, všechno ostatní je objekt:
| Typ | Příklady | typeof vrátí |
|---|---|---|
| string (text) | 'ahoj', "ahoj", `ahoj` | 'string' |
| number (číslo) | 42, 3.14, -1, NaN, Infinity | 'number' |
| boolean | true, false | 'boolean' |
| undefined | „hodnota zatím nebyla nastavena“ | 'undefined' |
| null | „záměrně prázdné“ | 'object' ⚠️ historická chyba |
| object | { name: 'Jana' }, [1, 2], new Date() | 'object' |
| function | () => {} | 'function' |
typeof 'ahoj' // 'string'typeof 42 // 'number'typeof true // 'boolean'typeof undefined // 'undefined'typeof { a: 1 } // 'object'typeof [1, 2] // 'object' – pole je taky objekt!typeof null // 'object' – chyba z roku 1995, která už zůstalaArray.isArray([1, 2]) // true – takhle se správně pozná polevalue === null // takhle se správně pozná null
JavaScript má pro celá i desetinná čísla jediný typ number.
10 + 3 // 1310 - 3 // 710 * 3 // 3010 / 3 // 3.333333333333333510 % 3 // 1 – zbytek po dělení (modulo)2 ** 10 // 1024 – mocninaMath.round(2.5) // 3 – zaokrouhlíMath.floor(2.9) // 2 – dolůMath.ceil(2.1) // 3 – nahoruMath.trunc(-2.9) // -2 – useknutí desetinné částiMath.abs(-5) // 5Math.min(4, 1, 9) // 1Math.max(4, 1, 9) // 9Math.random() // náhodné číslo od 0 (včetně) do 1 (bez)
7 % 2 === 1 // liché číslo8 % 2 === 0 // sudé čísloconst seconds = 187Math.floor(seconds / 60) // 3 – celé minutyseconds % 60 // 7 – zbylé sekundy → „3:07“index % 3 // 0, 1, 2, 0, 1, 2… – cyklické střídání (třeba barev)
0.1 + 0.2 // 0.30000000000000004 😱0.1 + 0.2 === 0.3 // false(0.1 + 0.2).toFixed(2) // '0.30' – POZOR, vrací TEXTMath.round((0.1 + 0.2) * 100) / 100 // 0.3 – číslo zaokrouhlené na 2 místa
Počítač ukládá čísla ve dvojkové soustavě a 0.1 v ní neumí zapsat přesně – podobně jako my neumíme přesně zapsat 1/3 desetinným číslem. U peněz se proto často počítá v haléřích (celá čísla) a na koruny se převádí až při zobrazení.
Number('ahoj') // NaN0 / 0 // NaNNaN === NaN // false – NaN se nerovná ani sám sobě!Number.isNaN(NaN) // true – jediný spolehlivý test10 / 0 // Infinity (ne chyba)
Text můžeš zapsat do jednoduchých '…', dvojitých "…" nebo zpětných `…` uvozovek. Zpětné mají superschopnost: dá se do nich vložit hodnota.
const name = 'Jana'const count = 3'Ahoj, ' + name + '! Máš ' + count + ' zprávy.' // postaru – snadno zapomeneš mezeru`Ahoj, ${name}! Máš ${count} zprávy.` // šablona – ${…} vloží hodnotu`Celkem: ${count * 12} Kč` // uvnitř ${…} může být libovolný výraz`Stav: ${count > 0 ? 'něco máš' : 'prázdno'}`const html = `<ul><li>Šablona může mít víc řádků</li></ul>`
Řetězce se nedají změnit – každá metoda vrací nový text a původní nechá být.
const text = ' Ahoj Světe 'text.length // 14 – počet znaků (i s mezerami)text.trim() // 'Ahoj Světe' – bez mezer na krajíchtext.toUpperCase() // ' AHOJ SVĚTE 'text.toLowerCase() // ' ahoj světe 'const s = 'Ahoj Světe's[0] // 'A' – znak na pozici 0 (počítá se od nuly!)s.at(-1) // 'e' – poslední znaks.includes('Svět') // trues.startsWith('Ahoj') // trues.endsWith('!') // falses.indexOf('S') // 5 – kde začíná; -1 když nenajdes.slice(0, 4) // 'Ahoj' – od pozice 0 do 4 (bez 4)s.slice(-5) // 'Světe' – posledních 5 znakůs.replace('Světe', 'Jano') // 'Ahoj Jano' – nahradí PRVNÍ výskyt'a-b-c'.replaceAll('-', '/') // 'a/b/c' – nahradí všechny'Jana Nováková'.split(' ') // ['Jana', 'Nováková'] – rozdělí na pole['a', 'b', 'c'].join(', ') // 'a, b, c' – opak: spojí pole do textu'7'.padStart(2, '0') // '07' – doplní zleva na délku 2'ha'.repeat(3) // 'hahaha'
'a' < 'b' // true – porovnává podle kódů znaků'Z' < 'a' // true 😕 velká písmena mají menší kód'č' > 'd' // true 😕 háčky jsou až za anglickou abecedou'č'.localeCompare('d', 'cs') // -1 – „č patří před d“ podle češtiny ✅'Jana'.toLowerCase() === 'jana' // porovnání bez ohledu na velikost písmen
Všechno, co uživatel napíše do formuláře, přijde jako text. I když napíše „42“. Chceš-li s tím počítat, musíš to převést:
Number('42') // 42Number('3.14') // 3.14Number('3,14') // NaN – čárka není desetinná tečka!Number('') // 0 – pozor, prázdný text je nulaNumber(' 7 ') // 7 – mezery na krajích nevadíNumber('12px') // NaNparseInt('12px') // 12 – čte číslice zleva, dokud to jdeparseFloat('3.5kg') // 3.5parseInt('3.9') // 3 – jen celá částString(42) // '42'(42).toString() // '42'42 + '' // '42' – funguje, ale String() je čitelnějšíBoolean('ahoj') // trueBoolean('') // false!!'ahoj' // true – dvojitá negace je zkratka za Boolean()
Když JavaScript dostane nečekaný typ, zkusí ho sám převést. Výsledky občas překvapí:
'5' + 3 // '53' – plus s textem SPOJUJE texty'5' - 3 // 2 – mínus umí jen čísla, tak text převede'5' * '2' // 10true + 1 // 2[] + [] // '''10' > '9' // false 😱 porovnává texty znak po znaku: '1' < '9'10 > 9 // true
Co vidíš: Textové pole a pod ním, co z jeho obsahu udělají různé převody. Tak přesně přicházejí hodnoty z formulářů – vždy jako text.
Vyzkoušej:
text + 1 dá '421' (spojení textů), ale text * 1 dá číslo 42 – násobení text převede na číslo.Number vrátí NaN, parseFloat jen 3. Pomůže až nahrazení čárky tečkou.Number to vzdá, parseInt přečte 12.Number('') je 0, ale Boolean('') je false. A '' == 0 je dokonce true.Co z toho plyne: Text z formuláře vždy převeď sám a zkontroluj Number.isNaN. Spoléhat na automatické převody je cesta k chybám.
Načítám interaktivní část…
Dvě rovnítka (==) před porovnáním převádějí typy podle složitých pravidel. Tři rovnítka (===) porovnají typ i hodnotu – bez překvapení.
| Výraz | == (volné) | === (přísné) |
|---|---|---|
5 vs '5' | true | false |
0 vs '' | true | false |
0 vs false | true | false |
null vs undefined | true | false |
NaN vs NaN | false | false |
5 === 5 // true5 === '5' // false – jiný typ5 !== '5' // true – „nerovná se“ v přísné verziage >= 18 // větší nebo rovnoage < 65 && age >= 18 // a zároveň (víc v lekci o podmínkách)
Dvě úplně krátké funkce na zahřátí: pozdrav se jménem a dvojnásobek čísla.
VÝZVA: Pozdrav
1. greet('Jana') má vrátit 'Ahoj, Jana!'. Použij šablonový řetězec: `Ahoj, ${name}!`
2. double(4) má vrátit 8.Načítám interaktivní část…
Napiš šest malých funkcí na práci s čísly a texty – přesně takové, jaké potřebuješ při zpracování formulářů. Testy se spustí samy a ukážou, které už fungují.
VÝZVA: Textoví pomocníci (čísla, řetězce, převody)
Napiš těla šesti malých funkcí. Testy pod nimi se spustí samy a ukážou ✅/❌.
1. initials('Jana Nováková') → 'JN'
Rozděl jméno podle mezery (split), vezmi první písmeno obou částí ([0]) a zvětši (toUpperCase).
2. formatTime(187) → '03:07' (sekundy → minuty:sekundy)
Math.floor(s / 60) jsou minuty, s % 60 sekundy. Obojí doplň nulou zleva na 2 znaky (String(…).padStart(2, '0')).
3. parseAmount('12,5') → 12.5, parseAmount('abc') → 0
Čárku nahraď tečkou (replace), převeď přes Number(). Když vyjde NaN (Number.isNaN), vrať 0.
4. roundTo2(3.14159) → 3.14
Vynásob stem, zaokrouhli (Math.round) a vyděl stem. Vrať číslo, ne text!
5. shout(' ahoj ') → 'AHOJ!'
Ořízni mezery (trim), zvětši písmena a přidej vykřičník (šablonový řetězec).
6. isValidEmail('jana@example.cz') → true, isValidEmail('jana.example.cz') → false
Stačí jednoduchá kontrola: obsahuje '@' (includes) a nekončí ani nezačíná zavináčem (startsWith/endsWith).Načítám interaktivní část…
const všude, let jen když hodnotu přepisuješ, var nikdy. Proměnná platí v bloku { … }.Array.isArray.0.1 + 0.2). Zaokrouhluj Math.round(x * 100) / 100, toFixed vrací text.`…${x}…` místo spojování plusem. Metody řetězců vracejí nový text.Number() a hlídej NaN.===, nikdy ==.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>)}
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>)}