Jak vÃme z kapitoly Datové typy, JavaScript obsahuje osm datových typů. Sedm z nich se nazývá âprimitivnà typyâ nebo âprimitivyâ, protože jejich hodnoty obsahujà pouze jednu vÄc (aÅ¥ už je to ÅetÄzec, ÄÃslo nebo nÄco jiného).
Naproti tomu objekty se použÃvajà k uloženà kolekcà různých dat pod klÃÄi a složitÄjÅ¡Ãch entit. V JavaScriptu objekty pronikajà do témÄÅ vÅ¡ech aspektů jazyka. MusÃme jim tedy porozumÄt pÅedtÃm, než půjdeme do hloubky v nÄÄem jiném.
Objekt můžeme vytvoÅit pomocà složených závorek {â¦} obsahujÃcÃch nepovinný seznam vlastnostÃ. Vlastnost je dvojice âklÃÄ: hodnotaâ, v nÞ klÃÄ je ÅetÄzec (nazývá se také ânázev vlastnostiâ) a hodnota může být cokoli.
Objekt si můžeme pÅedstavit jako skÅÃÅ s oznaÄenými spisy. Každý kousek dat je uložen podle klÃÄe ve svém spisu. Je pak lehké najÃt spis podle jeho názvu nebo spis pÅidat Äi odstranit.
Prázdný objekt (âprázdná skÅÃÅâ) může být vytvoÅen pomocà jedné ze dvou syntaxÃ:
let uživatel = new Object(); // syntaxe âkonstruktor objektuâ
let uživatel = {}; // syntaxe âobjektový literálâ
Obvykle se použÃvajà složené závorky {...}. Tato deklarace se nazývá objektový literál.
Literály a vlastnosti
Do {...} můžeme rovnou umÃstit vlastnosti jako dvojice âklÃÄ: hodnotaâ:
let uživatel = { // objekt
jméno: "Jan", // pod klÃÄem âjménoâ uložÃme hodnotu âJanâ
vÄk: 30 // pod klÃÄem âvÄkâ uložÃme hodnotu 30
};
Vlastnost má klÃÄ (nazývaný také âjménoâ, ânázevâ nebo âidentifikátorâ) pÅed dvojteÄkou ":" a hodnotu napravo od nÃ.
V objektu uživatel se nacházejà dvÄ vlastnosti:
- Prvnà vlastnost má název
"jméno"a hodnotu"Jan". - Druhá vlastnost má název
"vÄk"a hodnotu30.
Výsledný objekt uživatel si můžeme pÅedstavit jako skÅÃÅ se dvÄma spisy oznaÄenými âjménoâ a âvÄkâ.
Kdykoli do nà můžeme pÅidávat, odebÃrat a ÄÃst z nà spisy.
Hodnoty vlastnostà jsou dostupné pomocà teÄkové notace:
// vrátà hodnoty vlastnostà objektu:
alert( uživatel.jméno ); // Jan
alert( uživatel.vÄk ); // 30
Hodnota může být libovolného typu. PÅidejme hodnotu typu boolean:
uživatel.jeSprávce = true;
K odstranÄnà vlastnosti můžeme použÃt operátor delete:
delete uživatel.vÄk;
Můžeme použÃt i vÃceslovné názvy vlastnostÃ, ale pak je musÃme dát do uvozovek:
let uživatel = {
jméno: "Jan",
vÄk: 30,
"má rád ptáky": true // vÃceslovný název vlastnosti musà být v uvozovkách
};
Poslednà vlastnost v seznamu může konÄit Äárkou:
let uživatel = {
jméno: "Jan",
vÄk: 30,
}
Tato Äárka se nazývá âvleÄnáâ. DÃky nà je jednoduššà pÅidávat, odebÃrat nebo pÅesunovat vlastnosti, protože vÅ¡echny Åádky budou vypadat podobnÄ.
Hranaté závorky
U vÃceslovných vlastnostà teÄkový pÅÃstup nefunguje:
// toto způsobà syntaktickou chybu
uživatel.má rád ptáky = true
JavaScript tomu nerozumÃ. Myslà si, že adresujeme uživatel.má, a pak oznámà syntaktickou chybu, když narazà na neÄekané rád.
TeÄka vyžaduje, aby klÃÄ byl platný identifikátor promÄnné. To znamená, že neobsahuje žádné mezery, nezaÄÃná ÄÃslicà a neobsahuje speciálnà znaky ($ a _ jsou povoleny).
Existuje alternativnà ânotace hranatých závorekâ, která funguje s libovolným ÅetÄzcem:
let uživatel = {};
// nastavit
uživatel["má rád ptáky"] = true;
// naÄÃst
alert(uživatel["má rád ptáky"]); // true
// smazat
delete uživatel["má rád ptáky"];
Nynà je vÅ¡echno v poÅádku. VÅ¡imnÄte si, že ÅetÄzec uvnitÅ hranatých závorek je správnÄ ohraniÄen uvozovkami (lze použÃt jakýkoli druh uvozovek).
Hranaté závorky také poskytujà způsob, jak zÃskat název vlastnosti jako výsledek jakéhokoli výrazu â na rozdÃl od literálnÃho ÅetÄzce â napÅÃklad z promÄnné následovnÄ:
let klÃÄ = "má rád ptáky";
// totéž jako uživatel["má rád ptáky"] = true;
uživatel[klÃÄ] = true;
Zde může být promÄnná klÃÄ vypoÄtena za bÄhu skriptu nebo záviset na uživatelském vstupu. A pak ji použijeme k pÅÃstupu k vlastnosti. To nám dává velké množstvà flexibility.
NapÅÃklad:
let uživatel = {
jméno: "Jan",
vÄk: 30
};
let klÃÄ = prompt("Co chcete vÄdÄt o uživateli?", "jméno");
// pÅÃstup pomocà promÄnné
alert( uživatel[klÃÄ] ); // Jan (pokud bylo zadáno "jméno")
TeÄkovou notaci nelze podobným způsobem použÃt:
let uživatel = {
jméno: "Jan",
vÄk: 30
};
let kl̀ = "jm̩no";
alert( uživatel.klÃÄ ) // undefined
VypoÄÃtávané vlastnosti
Když vytváÅÃme objekt, můžeme použÃt hranaté závorky v objektovém literálu. Takové vlastnosti se nazývajà vypoÄÃtávané.
NapÅÃklad:
let ovoce = prompt("Jaké ovoce koupit?", "jablko");
let taška = {
[ovoce]: 5, // název vlastnosti se zÃská z promÄnné ovoce
};
alert( taška.jablko ); // 5, je-li ovoce=="jablko"
Význam vypoÄÃtávané vlastnosti je jednoduchý: [ovoce] znamená, že název vlastnosti by mÄl být pÅevzat z promÄnné ovoce.
Jestliže tedy návÅ¡tÄvnÃk zadá "jablko", taÅ¡ka bude {jablko: 5}.
V zásadÄ to funguje stejnÄ jako:
let ovoce = prompt("Jaké ovoce koupit?", "jablko");
let taška = {};
// vezmeme název vlastnosti z promÄnné ovoce
taška[ovoce] = 5;
â¦Ale vypadá to lépe.
UvnitÅ hranatých závorek můžeme použÃvat i složitÄjšà výrazy:
let ovoce = 'apple'; // anglicky âjablkoâ (pozn. pÅekl.)
let taška = {
[ovoce + 'Computers']: 5 // taška.appleComputers = 5
};
Hranaté závorky jsou mnohem silnÄjšà než teÄková notace. UmožÅujà libovolné názvy vlastnostà a promÄnné. Je vÅ¡ak také pracnÄjšà je napsat.
VÄtÅ¡inou se tedy použÃvá teÄka, jsou-li názvy vlastnostà známé a jednoduché. Teprve když potÅebujeme nÄco složitÄjÅ¡Ãho, pÅejdeme k hranatým závorkám.
Zkratka hodnoty vlastnosti
Ve skuteÄném kódu Äasto použÃváme jako hodnoty vlastnostà již existujÃcà promÄnné.
NapÅÃklad:
function vytvoÅUživatele(jméno, vÄk) {
return {
jméno: jméno,
vÄk: vÄk,
// ...dalšà vlastnosti
};
}
let uživatel = vytvoÅUživatele("Jan", 30);
alert(uživatel.jméno); // Jan
Ve výše uvedeném pÅÃkladu majà vlastnosti stejné názvy jako promÄnné. VytvoÅenà vlastnosti z promÄnné se provádà natolik Äasto, že pro nÄj existuje speciálnà zkratka hodnoty vlastnosti, která jej zkracuje.
MÃsto jméno:jméno můžeme napsat jen jméno, napÅÃklad takto:
function vytvoÅUživatele(jméno, vÄk) {
return {
jméno, // totéž jako jméno: jméno
vÄk, // totéž jako vÄk: vÄk
// ...
};
}
Ve stejném objektu můžeme použÃt bÄžné vlastnosti i zkratky:
let uživatel = {
jméno, // totéž jako jméno: jméno
vÄk: 30
};
Omezenà názvů vlastnostÃ
Jak už vÃme, promÄnná nemůže mÃt stejný název jako nÄkteré z rezervovaných slov, napÅ. âforâ, âletâ, âreturnâ atd.
Pro vlastnost objektu vÅ¡ak toto omezenà neplatÃ:
// vÅ¡echny tyto vlastnosti jsou správnÄ
let obj = {
for: 1,
let: 2,
return: 3
};
alert( obj.for + obj.let + obj.return ); // 6
Krátce ÅeÄeno, názvy vlastnostà nejsou nijak omezeny. Mohou to být libovolné ÅetÄzce nebo symboly (speciálnà typ identifikátorů, který bude objasnÄn pozdÄji).
Ostatnà typy se automaticky konvertujà na ÅetÄzce.
NapÅÃklad když je jako klÃÄ vlastnosti použito ÄÃslo 0, stane se ÅetÄzcem 0:
let obj = {
0: "test" // totéž jako "0": "test"
};
// oba alerty pÅistupujà ke stejné vlastnosti (ÄÃslo 0 se pÅevede na ÅetÄzec "0")
alert( obj["0"] ); // test
alert( obj[0] ); // test (stejná vlastnost)
Je tady drobné úskalà se speciálnà vlastnostà jménem __proto__. Tu nemůžeme nastavit na neobjektovou hodnotu:
let obj = {};
obj.__proto__ = 5; // pÅiÅadÃme ÄÃslo
alert(obj.__proto__); // [object Object] - hodnota je objekt, nefungovalo to tak, jak jsme zamýšleli
Jak vidÃme z kódu, pÅiÅazenà primitivu 5 se ignoruje.
V dalÅ¡Ãch kapitolách vysvÄtlÃme zvláštnà povahu __proto__ a naznaÄÃme způsoby, jak takové chovánà opravit.
Test existence vlastnosti, operátor âinâ
Pozoruhodnou vlastnostà objektů v JavaScriptu, na rozdÃl od mnoha jiných jazyků, je možnost pÅistupovat k libovolné vlastnosti. Pokud vlastnost nebude existovat, nenastane chyba!
NaÄtenà neexistujÃcà vlastnosti jednoduÅ¡e vrátà undefined. Můžeme tedy snadno otestovat, zda vlastnost existuje:
let uživatel = {};
alert( uživatel.takováVlastnostNenà === undefined ); // true znamená âtaková vlastnost neexistujeâ
K tomuto úÄelu existuje i speciálnà operátor "in".
Jeho syntaxe je:
"klÃÄ" in objekt
NapÅÃklad:
let uživatel = { jméno: "Jan", vÄk: 30 };
alert( "vÄk" in uživatel ); // true, uživatel.vÄk existuje
alert( "blabla" in uživatel ); // false, uživatel.blabla neexistuje
ProsÃme vÅ¡imnÄte si, že na levé stranÄ in musà být název vlastnosti, což je obvykle ÅetÄzec v uvozovkách.
VypustÃme-li uvozovky, znamená to promÄnnou, která by mÄla obsahovat skuteÄný název, který bude provÄÅen. NapÅÃklad:
let uživatel = { vÄk: 30 };
let klÃÄ = "vÄk";
alert( klÃÄ in uživatel ); // true, vlastnost âvÄkâ existuje
ProÄ vůbec existuje operátor in? NestaÄilo by porovnávat s undefined?
Ve vÄtÅ¡inÄ pÅÃpadů porovnánà s undefined funguje správnÄ. Existuje vÅ¡ak speciálnà pÅÃpad, kdy selže, ale operátor "in" funguje korektnÄ.
Je to tehdy, když vlastnost objektu existuje, ale je v nà uloženo undefined:
let obj = {
test: undefined
};
alert( obj.test ); // je undefined, takže - taková vlastnost neexistuje?
alert( "test" in obj ); // true, vlastnost existuje!
Ve výše uvedeném kódu vlastnost obj.test technicky existuje, takže operátor in funguje správnÄ.
Takové situace nastávajà velmi zÅÃdka, jelikož undefined by nemÄlo být výslovnÄ pÅiÅazováno. Pro âneznáméâ nebo âprázdnéâ hodnoty vÄtÅ¡inou použÃváme null. Proto je operátor in v kódu exotickým hostem.
Cyklus âfor..inâ
Pro procházenà vÅ¡emi klÃÄi objektu existuje speciálnà forma cyklu: for..in. Je to úplnÄ nÄco jiného než konstrukce for(;;), kterou jsme již prostudovali.
Syntaxe:
for (klÃÄ in objekt) {
// tÄlo se spustà pro každý klÃÄ mezi vlastnostmi objektu
}
VypiÅ¡me napÅÃklad vÅ¡echny vlastnosti objektu uživatel:
let uživatel = {
jméno: "Jan",
vÄk: 30,
jeSprávce: true
};
for (let klÃÄ in uživatel) {
// klÃÄe
alert( klÃÄ ); // jméno, vÄk, jeSprávce
// hodnoty klÃÄů
alert( uživatel[klÃÄ] ); // Jan, 30, true
}
VÅ¡imnÄte si, že vÅ¡echny konstrukce âforâ nám umožÅujà deklarovat uvnitÅ cyklu smyÄkovou promÄnnou, jako zde let klÃÄ.
Můžeme zde použÃt i jiný název promÄnné namÃsto klÃÄ. NapÅÃklad hojnÄ se použÃvá "for (let vlastnost in obj)".
SeÅazené jako objekt
Jsou objekty seÅazené? Jinými slovy: když cyklus procházà nad objektem, obdržÃme vÅ¡echny vlastnosti ve stejném poÅadÃ, v jakém byly pÅidány? Můžeme se na to spolehnout?
Krátká odpovÄÄ je: âseÅazeny speciálnÃm způsobemâ: celoÄÃselné vlastnosti jsou seÅazeny, ostatnà se objevà v poÅadà vytvoÅenÃ. Následujà podrobnosti.
Uvažujme napÅÃklad objekt s telefonnÃmi pÅedvolbami států:
let pÅedvolby = {
"49": "NÄmecko",
"41": "Švýcarsko",
"44": "Velká Británie",
// ..,
"1": "USA"
};
for (let pÅedvolba in pÅedvolby) {
alert(pÅedvolba); // 1, 41, 44, 49
}
Objekt můžeme použÃt, abychom uživateli navrhli seznam možnostÃ. NapÅÃklad vytváÅÃme-li stránku zejména pro návÅ¡tÄvnÃky z NÄmecka, budeme pravdÄpodobnÄ chtÃt, aby jako prvnà bylo 49.
Jenže když kód spustÃme, uvidÃme úplnÄ jiný obrázek:
- jako prvnà bude USA (1)
- pak Švýcarsko (41) a tak dále.
Telefonnà pÅedvolby jsou seÅazeny vzestupnÄ, protože jsou to celá ÄÃsla. UvidÃme tedy 1, 41, 44, 49.
Pojem âceloÄÃselná vlastnostâ zde znamená ÅetÄzec, který může být konvertován na celé ÄÃslo a zpÄt beze zmÄny.
Takže "49" je název celoÄÃselné vlastnosti, protože když se pÅevede na celé ÄÃslo a zpÄt, zůstane stejný. Ale "+49" a "1.2" nejsou:
// Number(...) explicitnÄ konvertuje na ÄÃslo
// Math.trunc je vestavÄná funkce, která odstranà desetinnou Äást
alert( String(Math.trunc(Number("49"))) ); // "49", totéž, celoÄÃselná vlastnost
alert( String(Math.trunc(Number("+49"))) ); // "49", nenà totéž jako "+49" â nenà to celoÄÃselná vlastnost
alert( String(Math.trunc(Number("1.2"))) ); // "1", nenà totéž jako "1.2" â nenà to celoÄÃselná vlastnost
â¦Na druhou stranu nejsou-li klÃÄe celá ÄÃsla, budou seÅazeny v tomtéž poÅadÃ, v jakém byly vytvoÅeny, napÅÃklad:
let uživatel = {
jméno: "Jan",
pÅÃjmenÃ: "Novák"
};
uživatel.vÄk = 25; // pÅidáme dalÅ¡Ã
// jiné než celoÄÃselné vlastnosti jsou seÅazeny v poÅadÃ, v nÄmž byly vytvoÅeny
for (let vlastnost in uživatel) {
alert( vlastnost ); // jméno, pÅÃjmenÃ, vÄk
}
Abychom tedy vyÅeÅ¡ili problém s telefonnÃmi pÅedvolbami, můžeme âpodvádÄtâ tak, že pÅedvolby uÄinÃme neceloÄÃselnými. PostaÄà pÅidat znaménko plus "+" pÅed každou pÅedvolbu.
NapÅÃklad:
let pÅedvolby = {
"+49": "NÄmecko",
"+41": "Švýcarsko",
"+44": "Velká Británie",
// ..,
"+1": "USA"
};
for (let pÅedvolba in pÅedvolby) {
alert( +pÅedvolba ); // 49, 41, 44, 1
}
Nynà to funguje tak, jak jsme zamýšleli.
ShrnutÃ
Objekty jsou asociativnà pole s nÄkolika speciálnÃmi vlastnostmi.
Ukládajà se do nich vlastnosti (dvojice klÃÄ-hodnota), v nichž:
- KlÃÄe vlastnostà musejà být ÅetÄzce nebo symboly (obvykle to jsou ÅetÄzce).
- Hodnoty mohou být jakéhokoli typu.
Pro pÅÃstup k vlastnosti můžeme použÃt:
- TeÄkovou notaci:
obj.vlastnost. - Notaci hranatých závorek
obj["vlastnost"]. Hranaté závorky nám umožÅujà pÅevzÃt klÃÄ z promÄnné, napÅ.obj[promÄnnáSKlÃÄem].
Dalšà operátory:
- K vymazánà vlastnosti:
delete obj.vlastnost. - K ovÄÅenÃ, zda vlastnost se zadaným klÃÄem existuje:
"klÃÄ" in obj. - K iteraci nad objektem: cyklus
for (let klÃÄ in obj).
To, co jsme prostudovali v této kapitole, se nazývá âplaný objektâ nebo jen Object.
V JavaScriptu vÅ¡ak existuje i mnoho dalÅ¡Ãch druhů objektů:
Array(pole) k ukládánà seÅazených kolekcà dat,Date(datum) k ukládánà informacà o datu a Äasu,Error(chyba) k ukládánà informacà o chybÄ,- â¦a tak dále.
Majà své speciálnà vlastnosti, které prostudujeme pozdÄji. NÄkdy lidé ÅÃkajà âtyp Arrayâ nebo âtyp Dateâ, ale formálnÄ to nejsou samostatné typy, nýbrž patÅà k jednoduchému datovému typu âobjektâ a různými způsoby jej rozÅ¡iÅujÃ.
Objekty v JavaScriptu jsou velmi silné. Tady jsme jen lehce nakousli téma, které je opravdu obrovské. V dalÅ¡Ãch Äástech tohoto tutoriálu budeme s objekty pracovat blÞe a dozvÃme se o nich vÃc.
KomentáÅe
<code>, pro nÄkolik Åádků je obalte znaÄkou<pre>, pro vÃce než 10 Åádků vložte odkaz na pÃskoviÅ¡tÄ (plnkr, jsbin, codepenâ¦)