Volitelné zÅetÄzenà ?. je bezpeÄný způsob, jak pÅistupovat k vnoÅeným vlastnostem objektu, i když vlastnost nacházejÃcà se mezi nimi neexistuje.
Problém âneexistujÃcà vlastnostiâ
Pokud jste teprve zaÄali ÄÃst tento tutoriál a uÄit se JavaScript, tento problém se vás možná jeÅ¡tÄ nedotkl, ale docházà k nÄmu pomÄrnÄ Äasto.
Jako pÅÃklad mÄjme objekt uživatel, který obsahuje informace o naÅ¡ich uživatelÃch.
VÄtÅ¡ina naÅ¡ich uživatelů má ve vlastnosti uživatel.adresa adresu s ulicà uživatel.adresa.ulice, ale nÄkteÅà ji neuvedli.
Když se v takovém pÅÃpadÄ pokusÃme zÃskat uživatel.adresa.ulice a uživatel je bez adresy, dostaneme chybu:
let uživatel = {}; // uživatel bez vlastnosti âadresaâ
alert(uživatel.adresa.ulice); // Chyba!
To je oÄekávaný výsledek. JavaScript takto funguje. Když uživatel.adresa je undefined, pokus o zÃskánà uživatel.adresa.ulice selže s chybou.
V mnoha praktických pÅÃpadech bychom vÅ¡ak zde radÄji zÃskali undefined mÃsto chyby (což znamená âžádná uliceâ).
â¦A jiný pÅÃklad. PÅi vývoji webů můžeme pomocà speciálnÃho volánà metody, napÅ. document.querySelector('.elem'), zÃskat objekt, který odpovÃdá urÄitému prvku webové stránky. Když na stránce takový prvek nenÃ, metoda vracà null.
// document.querySelector('.elem') je null, pokud tam žádný prvek nenÃ
let html = document.querySelector('.elem').innerHTML; // chyba, pokud je to null
OpÄt platÃ, že pokud tento prvek neexistuje, pÅi pÅÃstupu k vlastnosti .innerHTML z null dostaneme chybu. Ale v nÄkterých pÅÃpadech, kdy je nepÅÃtomnost prvku normálnÃ, bychom se této chybÄ rádi vyhnuli a prostÄ pÅijali za výsledek html = null.
Jak to můžeme udÄlat?
OÄividným ÅeÅ¡enÃm by bylo zkontrolovat hodnotu pomocà if nebo podmÃnÄného operátoru ? pÅed pÅÃstupem k jejà vlastnosti, napÅÃklad:
let uživatel = {};
alert(uživatel.adresa ? uživatel.adresa.ulice : undefined);
Funguje to, nenastala žádná chyba⦠Ale nenà to pÅÃliÅ¡ elegantnÃ. Jak vidÃme, âuživatel.adresaâ se v kódu objevuje dvakrát.
Takto by vypadalo totéž pro document.querySelector:
let html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null;
VidÃme, že hledánà prvku document.querySelector('.elem') se zde ve skuteÄnosti volá dvakrát. To nenà dobré.
Pro hloubÄji vnoÅené vlastnosti to bude jeÅ¡tÄ oÅ¡klivÄjÅ¡Ã, protože bude potÅeba vÃce opakovánÃ.
NapÅÃklad zkusme podobným způsobem zÃskat uživatel.adresa.ulice.název.
let uživatel = {}; // uživatel nemá adresu
alert(uživatel.adresa ? uživatel.adresa.ulice ? uživatel.adresa.ulice.název : null : null);
Je to oÅ¡klivé a ÄlovÄk může mÃt problémy takovému kódu porozumÄt.
Existuje trochu lepšà způsob, jak to napsat, a to pomocà operátoru &&:
let uživatel = {}; // uživatel nemá adresu
alert( uživatel.adresa && uživatel.adresa.ulice && uživatel.adresa.ulice.název ); // undefined (žádná chyba)
Spojenà celé cesty k vlastnosti ANDem sice zajistÃ, že vÅ¡echny komponenty existujà (pokud ne, vyhodnocenà se zastavÃ), ale ani to nenà ideálnÃ.
Jak vidÃte, názvy vlastnostà jsou v kódu stále zdvojeny, tj. ve výše uvedeném kódu se uživatel.adresa objevà tÅikrát.
Z tohoto důvodu bylo do jazyka pÅidáno volitelné zÅetÄzenà ?., aby tento problém vyÅeÅ¡ilo jednou provždy!
Volitelné zÅetÄzenÃ
Volitelné zÅetÄzenà ?. zastavà vyhodnocovánÃ, jestliže hodnota pÅed ?. je undefined nebo null, a vrátà undefined.
Dále v tomto Älánku budeme pro pÅehlednost ÅÃkat, že nÄco âexistujeâ, jestliže to nenà null ani undefined.
Jinými slovy, hodnota?.vlastnost:
- funguje jako
hodnota.vlastnost, jestližehodnotaexistuje, - v opaÄném pÅÃpadÄ (když
hodnotajeundefined/null) vrátÃundefined.
Toto je bezpeÄný způsob, jak pÅistoupit k uživatel.adresa.ulice pomocà ?.:
let uživatel = {}; // uživatel nemá adresu
alert( uživatel?.adresa?.ulice ); // undefined (bez chyby)
Kód je krátký a jasný, nenà v nÄm žádné zdvojenÃ.
Zde je pÅÃklad s document.querySelector:
let html = document.querySelector('.elem')?.innerHTML; // nenÃ-li žádný prvek, bude undefined
NaÄtenà adresy pomocà uživatel?.adresa funguje i tehdy, když objekt uživatel neexistuje:
let uživatel = null;
alert( uživatel?.adresa ); // undefined
alert( uživatel?.adresa.ulice ); // undefined
ProsÃme vÅ¡imnÄte si: syntaxe ?. umožÅuje, aby volitelná byla hodnota pÅed nÃ, ale žádná dalÅ¡Ã.
NapÅ. ?. v uživatel?.adresa.ulice.název umožÅuje, aby uživatel byl bezpeÄnÄ null/undefined (a v takovém pÅÃpadÄ vrátà undefined), ale to platà jen pro objekt uživatel. K dalÅ¡Ãm vlastnostem se pÅistupuje obvyklým způsobem. Chceme-li, aby nÄkterá z nich byla volitelná, musÃme nahradit dalšà . za ?..
MÄli bychom použÃvat ?. jen tehdy, když je v poÅádku, že nÄco neexistuje.
NapÅÃklad pokud podle logiky naÅ¡eho kódu musà objekt uživatel existovat, ale adresa je volitelná, pak bychom mÄli psát uživatel.adresa?.ulice, ale ne uživatel?.adresa?.ulice.
Pak pokud se stane, že uživatel bude nedefinovaný, uvidÃme programátorskou chybu a opravÃme ji. Kdybychom vÅ¡ak pÅehnanÄ použÃvali ?., mohly by se chyby v kódu neohlásit i tehdy, když to nenà vhodné, a ladÄnà by bylo obtÞnÄjÅ¡Ã.
?. musà být deklarovanáPokud promÄnná uživatel vůbec neexistuje, pak uživatel?.cokoli ohlásà chybu:
// ReferenceError: uživatel nenà definován
uživatel?.adresa;
PromÄnná musà být deklarovaná (tj. let/const/var uživatel nebo jako parametr funkce). Volitelné zÅetÄzenà funguje jen pro deklarované promÄnné.
ZkratovánÃ
Jak bylo ÅeÄeno, ?. okamžitÄ zastavà (âvyzkratujeâ) vyhodnocovánÃ, jestliže levá Äást neexistuje.
Jestliže tedy vpravo za ?. následujà dalšà volánà funkcà nebo operace, nevykonajà se.
NapÅÃklad:
let uživatel = null;
let x = 0;
uživatel?.ÅekniAhoj(x++); // âuživatelâ nenÃ, takže bÄh se nedostane k volánà ÅekniAhoj a x++
alert(x); // 0, hodnota se nezvýšila
Dalšà varianty: ?.(), ?.[]
Volitelné zÅetÄzenà ?. nenà operátor, ale speciálnà syntaktický konstrukt, který funguje i s funkcemi a hranatými závorkami.
NapÅÃklad ?.() se použÃvá k volánà funkce, která nemusà existovat.
V nÞe uvedeném kódu nÄkteÅà z naÅ¡ich uživatelů majà metodu správce a nÄkteÅà ne:
let uživatelSprávce = {
správce() {
alert("Jsem správce");
}
};
let uživatelHost = {};
uživatelSprávce.správce?.(); // Jsem správce
uživatelHost.správce?.(); // nic se nestane (taková metoda nenÃ)
Zde na obou ÅádcÃch nejprve použijeme teÄku (uživatelSprávce.správce) k zÃskánà vlastnosti správce, protože pÅedpokládáme, že objekt uživatel existuje, takže je bezpeÄné z nÄj ÄÃst.
Pak ?.() provÄÅà levou stranu: jestliže funkce správce existuje, pak se spustà (tak tomu je pro uživatelSprávce). Jinak (pro uživatelHost) se vyhodnocovánà zastavà bez chyb.
Funguje také syntaxe ?.[], jestliže pro pÅÃstup k vlastnostem radÄji použÃváme hranaté závorky [] namÃsto teÄky .. PodobnÄ jako v pÅedchozÃch pÅÃpadech nám umožÅuje bezpeÄnÄ naÄÃst vlastnost z objektu, který nemusà existovat.
let klÃÄ = "kÅestnÃJméno";
let uživatel1 = {
kÅestnÃJméno: "Jan"
};
let uživatel2 = null;
alert( uživatel1?.[klÃÄ] ); // Jan
alert( uživatel2?.[klÃÄ] ); // undefined
Můžeme použÃt ?. i s delete:
delete uživatel?.jméno; // delete uživatel.jméno, pokud uživatel existuje
?. k bezpeÄnému Ätenà a mazánÃ, ale ne k zápisuVolitelné zÅetÄzenà ?. nelze použÃt na levé stranÄ pÅiÅazenÃ.
NapÅÃklad:
let uživatel = null;
uživatel?.jméno = "Jan"; // Chyba, nefunguje to
// protože se to vyhodnotà jako: undefined = "Jan"
ShrnutÃ
Syntaxe volitelného zÅetÄzenà ?. má tÅi podoby:
obj?.vlastnostâ jestližeobjexistuje, vrátÃobj.vlastnost, jinak vrátÃundefined.obj?.[vlastnost]â jestližeobjexistuje, vrátÃobj[vlastnost], jinak vrátÃundefined.obj.metoda?.()â jestližeobj.metodaexistuje, zavoláobj.metoda(), jinak vrátÃundefined.
Jak vidÃme, vÅ¡echny jsou srozumitelné a snadno se použÃvajÃ. ?. ovÄÅÃ, zda levá strana je null/undefined, a pokud nenÃ, umožnà pokraÄovat ve vyhodnocovánÃ.
ÅetÄzec vÃce ?. nám umožÅuje bezpeÄný pÅÃstup k vnoÅeným vlastnostem.
PÅesto bychom mÄli použÃvat ?. opatrnÄ a jen tehdy, když je podle logiky naÅ¡eho kódu pÅijatelné, aby levá strana skuteÄnÄ neexistovala. Tak se pÅed námi neukryjà programátorské chyby, jestliže k nim dojde.
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â¦)