Lekce 0.8 · JavaScript obecně
Moduly a vestavěné nástroje
import/export, JSON, Math, Date, Intl, localStorage a třídy v kostce.
Načítám lekci…
Lekce 0.8 · JavaScript obecně
import/export, JSON, Math, Date, Intl, localStorage a třídy v kostce.
Načítám lekci…
Velký projekt je jako dílna. Kdyby všechno nářadí leželo na jedné hromadě, nic bys nenašel. Proto má každý druh svůj šuplík s popiskem – a když potřebuješ šroubovák, otevřeš šuplík „šroubováky“. V JavaScriptu jsou šuplíky moduly: každý soubor má své věci pro sebe a ven „půjčí“ jen to, co výslovně exportuje.
Druhá polovina lekce je prohlídka nářadí, které JavaScript dostal do vínku: JSON, Math, Date, Intl, localStorage a třídy. Nemusíš je umět nazpaměť – stačí vědět, že existují, a umět je najít.
Každý soubor je samostatný modul. Proměnné a funkce v něm jsou soukromé, dokud je neexportuješ. Existují dva druhy exportu:
// math.jsexport const PI = 3.14159 // pojmenovaný export (může jich být hodně)export function add(a, b) { return a + b }function secret() { … } // bez export – zvenku nedostupnéexport default function multiply(a, b) { // výchozí export (jen jeden na soubor)return a * b}
// app.jsimport multiply, { PI, add } from './math.js' // výchozí bez závorek, pojmenované ve { }import times from './math.js' // výchozí si můžeš pojmenovat jakkoliimport { add as plus } from './math.js' // pojmenovaný přejmenuješ přes asimport * as math from './math.js' // všechno pod jedním jménem: math.add, math.PI
| Pojmenovaný export | Výchozí export | |
|---|---|---|
| Kolik v souboru | libovolně | nejvýš jeden |
| Import | import { add } from './math' | import multiply from './math' |
| Jméno při importu | musí sedět (nebo as) | zvolíš si |
| V Reactu typicky | pomocné funkce, hooky, typy | komponenta souboru (třeba Lesson.tsx) |
import { useState } from 'react' // bez tečky = balíček z node_modulesimport { products } from '../course/fakeApi' // ./ a ../ = vlastní soubor, cesta relativně k tomuhleimport './styles.css' // import kvůli vedlejšímu efektu (načte styly)// Přípona .js/.ts/.tsx se v projektech s Vite obvykle vynechává
// components/index.js – jeden vstupní bod pro celou složkuexport { Button } from './Button'export { Card } from './Card'// jinde pak: import { Button, Card } from './components'// Dynamický import – načte modul až když je potřeba (vrací Promise)const { heavyChart } = await import('./charts.js')
JSON je textový zápis dat, který vypadá skoro jako JavaScriptový objekt. Používá se všude, kde data cestují: mezi serverem a prohlížečem, do souborů, do localStorage.
const user = { name: 'Jana', age: 31, tags: ['admin'] }const text = JSON.stringify(user)// '{"name":"Jana","age":31,"tags":["admin"]}' – jeden dlouhý TEXTJSON.stringify(user, null, 2) // hezky odsazený – na laděníJSON.parse(text) // zpátky na objekt (nový objekt!)
Math.round(4.5) // 5Math.floor(4.9) // 4 – dolůMath.ceil(4.1) // 5 – nahoruMath.trunc(-4.9) // -4 – useknout desetinnou částMath.abs(-7) // 7Math.max(3, 8, 1) // 8Math.min(...[3, 8, 1]) // 1 – s polem přes spreadMath.sqrt(16) // 42 ** 8 // 256 – mocnina (dříve Math.pow)// Náhodné celé číslo od min do max (včetně)const randomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + minrandomInt(1, 6) // kostka// Náhodná položka poleconst pick = (arr) => arr[Math.floor(Math.random() * arr.length)]// Omezení do rozsahuconst clamp = (n, min, max) => Math.min(Math.max(n, min), max)clamp(150, 0, 100) // 100
const now = new Date() // teďconst d = new Date(2026, 9, 4) // 4. ŘÍJNA 2026 – měsíce se počítají od 0! 😱const iso = new Date('2026-10-04') // z textu ve formátu ISO (rok-měsíc-den)const fromMs = new Date(1_700_000_000_000) // z počtu milisekund od 1. 1. 1970d.getFullYear() // 2026d.getMonth() // 9 – říjen (leden = 0)d.getDate() // 4 – den v měsícid.getDay() // 0–6 – den v týdnu (neděle = 0)d.getHours() // 0Date.now() // aktuální čas v ms – rychlé měření a jedinečná číslad.getTime() // tohle datum v msd.toISOString() // '2026-10-03T22:00:00.000Z' – standardní text pro servery (v UTC)
// Rozdíl dvou dat = rozdíl milisekundconst DAY = 24 * 60 * 60 * 1000const days = Math.round((new Date('2026-12-24') - new Date('2026-10-04')) / DAY) // 81// PorovnánídateA < dateB // funguje (porovná ms)dateA.getTime() === dateB.getTime() // shoda – === u dvou Date objektů je vždy false!// Přičíst dny – setDate MĚNÍ objekt, proto nejdřív kopieconst tomorrow = new Date(today)tomorrow.setDate(tomorrow.getDate() + 1) // přetečení přes konec měsíce Date vyřeší sám
Formátovat čísla a data ručně (mezery mezi tisíci, desetinná čárka, „Kč“ za číslem, názvy měsíců) je zbytečné. Prohlížeč to umí v každém jazyce:
const n = 1234567.891n.toLocaleString('cs-CZ') // '1 234 567,891'n.toLocaleString('en-US') // '1,234,567.891'new Intl.NumberFormat('cs-CZ', { style: 'currency', currency: 'CZK' }).format(n)// '1 234 567,89 Kč'new Intl.NumberFormat('cs-CZ', { maximumFractionDigits: 1 }).format(3.14159) // '3,1'new Intl.NumberFormat('cs-CZ', { style: 'percent' }).format(0.256) // '26 %'const date = new Date(2026, 9, 4)date.toLocaleDateString('cs-CZ') // '4. 10. 2026'date.toLocaleDateString('cs-CZ', { day: 'numeric', month: 'long' }) // '4. října'date.toLocaleTimeString('cs-CZ', { hour: '2-digit', minute: '2-digit' }) // '00:00'new Intl.RelativeTimeFormat('cs', { numeric: 'auto' }).format(-1, 'day') // 'včera'new Intl.ListFormat('cs').format(['Jana', 'Petr', 'Eva']) // 'Jana, Petr a Eva'
Co vidíš: Pole pro číslo a datum. Pod nimi, jak je naformátují vestavěné metody a Intl.
Vyzkoušej:
toFixed a toLocaleString('cs-CZ'): první dá desetinnou tečku a žádné mezery, druhé české formátování.RelativeTimeFormat napíše „včera“ / „zítra“.Co z toho plyne: Čísla a data pro lidi formátuj přes Intl a toLocale… s 'cs-CZ'. Ruční skládání textu je zbytečné a chybové.
Načítám interaktivní část…
localStorage je malé úložiště v prohlížeči. Data v něm přežijí obnovení stránky i zavření prohlížeče. Kurz si v něm pamatuje, které výzvy máš hotové.
localStorage.setItem('theme', 'dark') // ulož (klíč, hodnota)localStorage.getItem('theme') // 'dark'localStorage.getItem('neexistuje') // nulllocalStorage.removeItem('theme') // smaž// Umí jen TEXT – objekty přes JSONconst settings = { theme: 'dark', fontSize: 16 }localStorage.setItem('settings', JSON.stringify(settings))const loaded = JSON.parse(localStorage.getItem('settings') ?? 'null')// ❌ Bez JSONlocalStorage.setItem('settings', settings) // uloží '[object Object]'
Třída je forma na bábovky: popisuje, jak vypadá a co umí každý objekt, který podle ní vyrobíš (new). V moderním Reactu je skoro nepotkáš – komponenty se píšou jako funkce. Občas se ale objeví (error boundary v lekci 10, knihovny, starší kód), tak ať je poznáš.
class Counter {count = 0 // vlastnost s výchozí hodnotouconstructor(start) { // zavolá se při new Counter(…)this.count = start ?? 0 // this = objekt, který se právě vyrábí}increment() { // metodathis.count++return this.count}}const a = new Counter(10)a.increment() // 11a.count // 11class LimitedCounter extends Counter { // dědičnost: všechno z Counter + něco navícincrement() {if (this.count >= 3) return this.countreturn super.increment() // zavolej verzi z rodičovské třídy}}
Dvě vestavěné funkce: jedna z objektu Math, druhá z JSON.
VÝZVA: Zaokrouhli a ulož jako text
1. roundUp(4.2) má vrátit 5 – zaokrouhlení nahoru (Math.ceil).
2. toJson({ a: 1 }) má vrátit text '{"a":1}' (JSON.stringify).Načítám interaktivní část…
Sedm pomocných funkcí, jaké najdeš skoro v každém projektu: formátování ceny a data, omezení do rozsahu, náhodné číslo, posun data a ukládání do localStorage.
VÝZVA: Šuplík s nářadím (Intl, Math, Date, JSON, localStorage)
Napiš těla sedmi pomocných funkcí, jaké má skoro každá aplikace.
Testy pod nimi se spustí samy a ukážou ✅/❌.
1. formatPrice(1234.5) → '1 234,50 Kč'
new Intl.NumberFormat('cs-CZ', { style: 'currency', currency: 'CZK' }).format(…)
2. formatDate(new Date(2026, 9, 4)) → '4. 10. 2026' (pozor: měsíce se v Date počítají od 0!)
date.toLocaleDateString('cs-CZ')
3. clamp(n, min, max) – vrať n, ale ne menší než min a ne větší než max.
clamp(15, 0, 10) → 10, clamp(-3, 0, 10) → 0. Math.min a Math.max.
4. randomInt(min, max) – náhodné CELÉ číslo od min do max VČETNĚ obou.
Math.floor(Math.random() * (max - min + 1)) + min
5. addDays(date, days) – vrať NOVÉ datum posunuté o days dní. Původní datum neměň!
const copy = new Date(date); copy.setDate(copy.getDate() + days)
6. saveJSON(key, value) – ulož libovolnou hodnotu do localStorage (JSON.stringify).
7. loadJSON(key, fallback) – načti hodnotu z localStorage (JSON.parse). Když tam nic není
(getItem vrátí null) nebo je uložený text neplatný JSON, vrať fallback (try/catch).Načítám interaktivní část…
{ }, výchozí bez nich. Cesta s ./ je vlastní soubor, bez ní balíček.Math.floor(Math.random() * (max - min + 1)) + min.setDate mění objekt.toLocale…('cs-CZ') naformátují čísla, měny a data po česku.new, this, extends). V moderním Reactu je potkáš málokdy.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>)}