JavaScript 03 · JavaScript obecně
Objekty
Tečka vs. hranaté závorky, destrukturace, spread, Object.entries a dynamické klíče.
Načítám lekci…
JavaScript 03 · JavaScript obecně
Tečka vs. hranaté závorky, destrukturace, spread, Object.entries a dynamické klíče.
Načítám lekci…
Představ si kartotéku v knihovně. Každý lístek popisuje jednu knihu a má kolonky s nadpisy: název, autor, rok. Objekt v JavaScriptu je přesně takový lístek – sada pojmenovaných kolonek (vlastností), každá s nějakou hodnotou.
const book = {title: 'Malý princ', // klíč: hodnotaauthor: 'Saint-Exupéry',year: 1943,available: true,}
Objekty jsou všude: uživatel, produkt v košíku, nastavení aplikace, odpověď ze serveru. V Reactu jsou objekt i props, které komponenta dostane. Kdo umí s objekty šikovně zacházet, píše kratší a čitelnější kód.
Kolonku z lístku přečteš dvěma způsoby: tečkou, nebo hranatými závorkami, ve kterých je jméno kolonky jako text v uvozovkách.
const user = { name: 'Jana', city: 'Praha', 'favorite color': 'modrá' }user.name // 'Jana' – za tečku napíšeš jméno vlastnostiuser['name'] // 'Jana' – totéž, jen jméno je v závorkách jako textuser['favorite color'] // 'modrá' – jméno s mezerou jde napsat jen do závorekuser.favorite color // ❌ chyba – za tečkou nesmí být jméno s mezerou
Hranaté závorky umí něco, co tečka neumí: nemusí v nich být napevno napsaný text. Může v nich být proměnná – nebo jakýkoli výraz. JavaScript ho nejdřív vyhodnotí a teprve výsledný text použije jako jméno vlastnosti.
Představ si, že v knihovně dostaneš papírek s nápisem „rok“ a máš z lístku přečíst kolonku, která je na papírku napsaná. Nehledáš kolonku jménem „papírek“ – podíváš se, co na papírku stojí, a přečteš kolonku rok. Proměnná v hranatých závorkách je takový papírek:
const field = 'city' // proměnná field obsahuje TEXT 'city'user[field] // 'Praha' – JavaScript dosadí hodnotu: user['city']const klic = 'name' // proměnná se může jmenovat jakkoliuser[klic] // 'Jana' – user['name']user['ci' + 'ty'] // 'Praha' – výraz se nejdřív spočítá na 'city'
Pozor na uvozovky – stačí je přidat a hledá se úplně jiná vlastnost:
| Zápis | Co JavaScript udělá | Výsledek |
|---|---|---|
✅ user[field] | vezme hodnotu proměnné field ('city') a přečte user.city | 'Praha' |
❌ user['field'] | hledá vlastnost, která se doslova jmenuje „field“ – user žádnou takovou nemá | undefined |
❌ user.field | totéž co user['field'] | undefined |
K čemu to je? Třeba když píšeš funkci, která má umět přečíst kteroukoli vlastnost – kterou, to rozhodne až ten, kdo ji zavolá:
const read = (obj, field) => obj[field]read(user, 'name') // 'Jana'read(user, 'city') // 'Praha'// s obj.field by obě volání vrátila undefined – hledala by vlastnost „field“
Kdy tedy použít který zápis?
| Zápis | Kdy |
|---|---|
obj.name | Víš předem, kterou vlastnost chceš – to je většina případů. |
obj[key] | Kterou vlastnost číst, je uložené v proměnné – třeba v parametru funkce nebo při procházení objektu ve smyčce. |
const order = {id: 7,customer: { name: 'Petr', address: { city: 'Brno' } },}order.customer.address.city // 'Brno' – vnořené objekty čteš tečkou za tečkouorder.note // undefined – neexistující vlastnost není chybaorder.delivery.date // 💥 TypeError – delivery chybí (je undefined) a z undefined nejde číst .dateorder.delivery?.date // undefined – ?. zjistí, že delivery chybí, a .date už nečte (víc v lekci JavaScript 06)
const user = { name: 'Jana' }user.age = 31 // přidá novou vlastnostuser.name = 'Jana N.' // změní existujícíuser['city'] = 'Praha' // totéž se závorkamidelete user.age // smaže vlastnostuser // { name: 'Jana N.', city: 'Praha' }
Tři zápisy, které ti při vytváření objektů ušetří práci a uvidíš je v kódu na každém kroku: vlastnost ze stejnojmenné proměnné, metody a vypočítané klíče.
Když se proměnná jmenuje stejně jako vlastnost, kterou chceš vytvořit, nemusíš jméno psát dvakrát:
const name = 'Jana'const age = 31const user = { name: name, age: age } // dlouze: vlastnost name dostane hodnotu proměnné nameconst user = { name, age } // krátce: totéž// oba zápisy vytvoří { name: 'Jana', age: 31 }
Ve vlastnosti objektu může být i funkce – pak se jí říká metoda. Objekt counter níž má dvě vlastnosti: číslo count a metodu increment, která to číslo zvýší. I metoda má kratší zápis:
// dlouze: vlastnost increment, jejíž hodnota je funkceconst counter = {count: 0,increment: function () {this.count++ // this = objekt, na kterém metodu voláš (tady counter)},}// krátce: totéž, jen bez „: function“const counter = {count: 0,increment() {this.count++},}counter.increment() // metodu zavoláš přes objekt a tečkucounter.count // 1
Když jméno vlastnosti předem neznáš, protože je uložené v proměnné, dej ho při vytváření objektu do hranatých závorek:
const field = 'email'const value = 'jana@example.cz'const update = { [field]: value } // { email: 'jana@example.cz' }const wrong = { field: value } // { field: 'jana@example.cz' } – doslova „field“// Typické použití: jedna funkce pro všechna pole formulářefunction updateForm(form, field, value) {return { ...form, [field]: value }}updateForm({ name: '', email: '' }, 'name', 'Jana') // { name: 'Jana', email: '' }const prefix = 'color'const theme = { [`${prefix}Primary`]: 'blue' } // { colorPrimary: 'blue' }
Destrukturace je vybalení krabice: místo „vezmi krabici a z ní věc s nálepkou name“ řekneš rovnou „chci věci name a city“.
const user = { name: 'Jana', city: 'Praha', age: 31 }const { name, city } = user // dvě proměnné najednouconst { name: userName } = user // přejmenování: proměnná se bude jmenovat userNameconst { role = 'host' } = user // výchozí hodnota, když vlastnost chybí (nebo je undefined)const { name: n, role: r = 'host' } = user // přejmenování i výchozí hodnotaconst { age, ...rest } = user // ... posbírá zbylé vlastnosti: rest = { name: 'Jana', city: 'Praha' }// (rest je obyčejné jméno proměnné, může být i ...others)
const order = { id: 7, customer: { name: 'Petr', address: { city: 'Brno' } } }const { customer: { name, address: { city } } } = ordername // 'Petr'city // 'Brno'// customer ani address jako proměnné nevznikly – jen jsme jimi „prošli“
// Funkce dostane objekt a rovnou si z něj vybalí, co potřebujefunction formatUser({ name, city = 'neznámé' }) {return `${name} (${city})`}formatUser({ name: 'Jana', city: 'Praha', age: 31 }) // 'Jana (Praha)'// Přesně takhle vypadá komponenta v Reactu:function UserCard({ name, city }) { … }// Totéž jde i v callbacku. map zavolá funkci pro každou položku pole (víc v lekci JavaScript 04)// a tady je každá položka objekt uživatele – parametr si z něj rovnou vybalí name:const users = [{ name: 'Jana', city: 'Praha' }, { name: 'Petr', city: 'Brno' }]users.map((user) => user.name) // bez destrukturace: ['Jana', 'Petr']users.map(({ name }) => name) // s destrukturací: totéž
U pole se nevybaluje podle jména, ale podle pořadí: první proměnná dostane první položku, druhá druhou a tak dál. Jména proměnných si proto volíš sám.
const [first, second] = ['jablko', 'hruška', 'švestka']// first = 'jablko', second = 'hruška' (švestka zbyla a nikam se neuložila)const [, , third] = ['jablko', 'hruška', 'švestka']// čárky bez jména přeskočí první dvě položky: third = 'švestka'const [head, ...tail] = [1, 2, 3, 4]// head = 1, tail = [2, 3, 4] – ... posbírá zbytek, stejně jako u objektu
Proč to potřebuješ: v Reactu budeš skoro v každé komponentě psát tenhle řádek. Funkce useState vrací pole se dvěma položkami – aktuální hodnotu a funkci, která ji mění – a destrukturace je rovnou pojmenuje:
const [count, setCount] = useState(0)// useState(0) vrátí pole [0, funkce]// count = 0 (první položka), setCount = funkce na změnu (druhá položka)const [name, setName] = useState('Jana') // jména si volíš sám, rozhoduje pořadí
Co přesně useState dělá, se dozvíš v lekci o stavu. Teď stačí poznat, že jde o obyčejnou destrukturaci pole.
Tři tečky před objektem znamenají „vysyp sem všechny jeho vlastnosti“.
const user = { name: 'Jana', city: 'Praha' }const copy = { ...user } // mělká kopie: nový objekt, stejné vlastnosticopy === user // false – jsou to dva různé objektyconst moved = { ...user, city: 'Brno' } // kopie s přepsanou vlastnostíconst withAge = { ...user, age: 31 } // kopie s novou vlastnostíconst defaults = { theme: 'light', lang: 'cs', fontSize: 16 }const saved = { theme: 'dark' }const settings = { ...defaults, ...saved } // { theme: 'dark', lang: 'cs', fontSize: 16 }
Někdy má objekt nějakou vlastnost dostat jen za určité podmínky – třeba vlastnost role jen administrátor. Poskládáš to ze dvou věcí: spreadu, který už znáš, a výběru „buď, anebo“.
Zápis podmínka ? A : B znamená „když podmínka platí, vezmi A, jinak B“ (podrobně ho probírá lekce JavaScript 06). Tady vybírá mezi objektem s vlastností role a prázdným objektem – a spread pak to, co vyšlo, vysype do nového objektu:
const isAdmin = trueconst user = {name: 'Jana',...(isAdmin ? { role: 'admin' } : {}),}// krok za krokem, když isAdmin je true:// 1. závorka vybere { role: 'admin' }// 2. ... ho vysype do objektu → { name: 'Jana', role: 'admin' }// a když isAdmin je false:// 1. závorka vybere {} – prázdný objekt// 2. ... nemá co vysypat → { name: 'Jana' } – vlastnost role vůbec nevznikne
Proč ne jednoduše role: isAdmin ? 'admin' : undefined? Tak by vlastnost role vznikla vždycky, jen u ostatních uživatelů s hodnotou undefined. Se spreadem ji ostatní uživatelé vůbec nemají.
Na procházení objektu má JavaScript tři hotové funkce: Object.keys, Object.values a Object.entries. Na rozdíl od rest nebo field, které jsi pojmenovával sám, jsou tahle jména pevně daná – musíš je napsat přesně takhle, včetně velkého O v Object. Každá z nich vrátí pole (pole podrobně probere příští lekce):
| Funkce | Vrátí | Česky |
|---|---|---|
Object.keys(obj) | pole jmen vlastností | klíče |
Object.values(obj) | pole hodnot | hodnoty |
Object.entries(obj) | pole dvojic [jméno, hodnota] | položky |
const prices = { jablko: 12, hruška: 15, švestka: 8 }Object.keys(prices) // ['jablko', 'hruška', 'švestka']Object.values(prices) // [12, 15, 8]Object.entries(prices) // [['jablko', 12], ['hruška', 15], ['švestka', 8]]
Nejčastěji projdeš entries smyčkou for…of. Každá dvojice se rovnou destrukturuje do dvou proměnných – a jejich jména (fruit, price) si zase volíš sám:
for (const [fruit, price] of Object.entries(prices)) {console.log(`${fruit} stojí ${price} Kč`)}// jablko stojí 12 Kč// hruška stojí 15 Kč// švestka stojí 8 Kč
Pár dalších věcí, které se hodí:
Object.keys(prices).length // 3 – počet vlastnostíObject.keys({}).length === 0 // true – tak se pozná prázdný objekt'jablko' in prices // true – má objekt vlastnost 'jablko'?Object.hasOwn(prices, 'banán') // false – totéž jinak: má objekt vlastnost 'banán'?Object.fromEntries([['a', 1], ['b', 2]]) // { a: 1, b: 2 } – opak entries: z dvojic zpátky objekt
Když chceš změnit všechny hodnoty objektu, projdi entries a výsledek skládej do nového objektu. Metody polí jako map a filter přijdou v příští lekci, pak to půjde napsat kratčeji.
// Zdražit všechno o 10 %const result = {}for (const [fruit, price] of Object.entries(prices)) {result[fruit] = Math.round(price * 1.1) // jméno vlastnosti je v proměnné fruit → závorky}// result = { jablko: 13, hruška: 17, švestka: 9 }
Co vidíš: Objekt user a tlačítka s operacemi. Pro každou vidíš kód, vysvětlení a výsledek.
Vyzkoušej:
city se přejmenovalo na town a chybějící email dostal výchozí hodnotu.Co z toho plyne: Objekt jde číst tečkou i závorkami, vybalit destrukturací, kopírovat spreadem a převést na pole přes Object.entries.
Načítám interaktivní část…
Zatím každý objekt popisoval jednu věc – uživatele, objednávku, knihu. Objekt ale může sloužit i jako slovník. V česko-anglickém slovníku najdeš vlevo slovo, které hledáš, a vpravo si přečteš překlad. V objektu je vlevo klíč (co hledáš) a vpravo hodnota (co dostaneš) – a hledáš hranatými závorkami, protože hledané slovo je v proměnné.
Představ si e-shop: stav objednávky je uložený jako krátké anglické slovo ('new', 'paid', 'shipped') a na stránce chceš ukázat český popisek. Slovník k tomu vypadá takhle:
const STATUS_LABELS = {new: 'Nová', // klíč 'new' → hodnota 'Nová'paid: 'Zaplacená',shipped: 'Odeslaná',}const status = 'paid' // stav objednávky, třeba ze serveruSTATUS_LABELS[status] // 'Zaplacená' – najde klíč 'paid' a vrátí jeho hodnotuSTATUS_LABELS['lost'] // undefined – takový klíč ve slovníku není
Jméno velkými písmeny je jen zvyk: říká, že jde o pevnou tabulku, která se za běhu programu nemění. Aby funkce u neznámého stavu nevrátila undefined, přidá se ?? – „když vlevo nic není, vezmi to vpravo“ (podrobně v lekci JavaScript 06). Porovnej to s řadou podmínek, která dělá totéž:
// ❌ Řada podmínek – s každým novým stavem další řádek s iffunction statusLabel(status) {if (status === 'new') return 'Nová'if (status === 'paid') return 'Zaplacená'if (status === 'shipped') return 'Odeslaná'return 'Neznámý stav'}// ✅ Slovník – nový stav = nový řádek ve slovníku, funkce se neměníconst statusLabel = (status) => STATUS_LABELS[status] ?? 'Neznámý stav'statusLabel('paid') // 'Zaplacená'statusLabel('lost') // 'Neznámý stav'
Slovník se dá i průběžně plnit. Chceš spočítat hlasy: klíčem bude odpověď, hodnotou počet. Začneš s prázdným objektem a pro každý hlas přičteš jedničku:
const votes = ['ano', 'ne', 'ano', 'ano']const counts = {}for (const v of votes) {counts[v] = (counts[v] ?? 0) + 1// (counts[v] ?? 0) = dosavadní počet, nebo 0, když tahle odpověď ve slovníku ještě není}// průběh:// 'ano' → counts['ano'] ještě není → 0 + 1 → { ano: 1 }// 'ne' → counts['ne'] ještě není → 0 + 1 → { ano: 1, ne: 1 }// 'ano' → counts['ano'] je 1 → 1 + 1 → { ano: 2, ne: 1 }// 'ano' → counts['ano'] je 2 → 2 + 1 → { ano: 3, ne: 1 }counts // { ano: 3, ne: 1 }
Uživatele máš v poli a často potřebuješ najít toho s určitým id. Místo abys pokaždé procházel celé pole, postavíš si jednou slovník, kde klíčem je id a hodnotou celý uživatel. Pak už každého najdeš jedním přečtením:
const users = [{ id: 7, name: 'Jana' }, { id: 9, name: 'Petr' }]const usersById = {}for (const u of users) {usersById[u.id] = u // pod klíč id ulož celého uživatele}// usersById = { 7: { id: 7, name: 'Jana' }, 9: { id: 9, name: 'Petr' } }usersById[9] // { id: 9, name: 'Petr' }usersById[9].name // 'Petr'
Přečti dvě vlastnosti objektu a vyrob jeho kopii s jednou vlastností navíc.
Zadání najdeš v komentářích v kódu níže – na začátku souboru a u míst označených TODO.
Načítám interaktivní část…
Šest malých funkcí na práci s účtem uživatele, formulářem, košíkem a ceníkem. U každé je v komentáři v kódu, k čemu se hodí, ukázka použití a odkaz na část lekce, kde najdeš postup.
Střední a těžké výzvy se odemknou, jakmile se přihlásíš. Stejně jako celý kurz jsou zdarma – stačí e-mail, žádné heslo ani platba.
undefined, ale čtení z undefined spadne – pomůže ?..{ name } a vypočítaný klíč { [field]: value }.name: n), dát výchozí hodnotu (= 'host') i posbírat zbytek (...rest).Object.keys / values / entries převedou objekt na pole, fromEntries zpátky.