Volitelné řetězení ?.

Nedávný přírůstek Toto je nedávný přírůstek do jazyka. Staré prohlížeče mohou vyžadovat polyfilly.

Volitelné řetězení ?. je bezpečný způsob přístupu k vlastnostem vnořených objektů, i když mezilehlá vlastnost neexistuje.

Problém „neexistujícího majetku“

Pokud jste právě začali číst tutoriál a učit se JavaScript, možná se vás problém ještě nedotkl, ale je to docela běžné.

Jako příklad řekněme, že máme user objekty, které obsahují informace o našich uživatelích.

Většina našich uživatelů má adresy v user.address nemovitost s ulicí user.address.street , ale někteří je neposkytli.

V takovém případě, když se pokusíme získat user.address.street a uživatel náhodou nemá adresu, zobrazí se chyba:

let user = {}; // a user without "address" property

alert(user.address.street); // Error!

To je očekávaný výsledek. JavaScript funguje takto. Jako user.address je undefined , pokus o získání user.address.street selže s chybou.

V mnoha praktických případech bychom raději získali undefined místo chyby zde (což znamená „žádná ulice“).

…a další příklad. Při vývoji webu můžeme získat objekt, který odpovídá prvku webové stránky, pomocí volání speciální metody, například document.querySelector('.elem') a vrátí null když žádný takový prvek neexistuje.

// document.querySelector('.elem') is null if there's no element
let html = document.querySelector('.elem').innerHTML; // error if it's null

Ještě jednou, pokud prvek neexistuje, dostaneme chybu při přístupu k .innerHTML vlastnost null . A v některých případech, kdy je nepřítomnost prvku normální, bychom se rádi vyhnuli chybě a prostě přijali html = null jako výsledek.

Jak to můžeme udělat?

Zřejmým řešením by bylo zkontrolovat hodnotu pomocí if nebo podmíněný operátor ? , před přístupem k jeho vlastnosti, takto:

let user = {};

alert(user.address ? user.address.street : undefined);

Funguje to, není tam žádná chyba... Ale je to docela nevkusné. Jak můžete vidět, "user.address" se v kódu objeví dvakrát.

Zde je návod, jak by totéž vypadalo pro document.querySelector :

let html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null;

Vidíme, že prvek hledá document.querySelector('.elem') se tady vlastně volá dvakrát. Není dobré.

Pro hlouběji vnořené vlastnosti se stává ještě ošklivější, protože je potřeba více opakování.

Např. získáme user.address.street.name podobným způsobem.

let user = {}; // user has no address

alert(user.address ? user.address.street ? user.address.street.name : null : null);

To je prostě hrozné, člověk může mít dokonce problémy s pochopením takového kódu.

Existuje trochu lepší způsob, jak to napsat, pomocí && operátor:

let user = {}; // user has no address

alert( user.address && user.address.street && user.address.street.name ); // undefined (no error)

Spojení celé cesty k vlastnosti zajišťuje, že všechny komponenty existují (pokud ne, hodnocení se zastaví), ale také to není ideální.

Jak vidíte, názvy vlastností jsou v kódu stále duplikovány. Např. ve výše uvedeném kódu user.address se objeví třikrát.

To je důvod, proč volitelné řetězení ?. byl přidán do jazyka. Abychom tento problém vyřešili jednou provždy!

Volitelné řetězení

Volitelné řetězení ?. zastaví vyhodnocení, pokud je hodnota před ?. je undefined nebo null a vrátí undefined .

Dále v tomto článku pro stručnost budeme říkat, že něco „existuje“, pokud to není null a ne undefined .

Jinými slovy value?.prop :

  • funguje jako value.prop , pokud value existuje,
  • jinak (když value je undefined/null ) vrátí undefined .

Zde je bezpečný způsob přístupu k user.address.street pomocí ?. :

let user = {}; // user has no address

alert( user?.address?.street ); // undefined (no error)

Kód je krátký a čistý, nedochází k žádné duplicitě.

Zde je příklad s document.querySelector :

let html = document.querySelector('.elem')?.innerHTML; // will be undefined, if there's no element

Čtení adresy pomocí user?.address funguje, i když user objekt neexistuje:

let user = null;

alert( user?.address ); // undefined
alert( user?.address.street ); // undefined

Poznámka:?. syntaxe činí volitelnou hodnotu před ní, ale ne další.

Např. v user?.address.street.name ?. umožňuje user bezpečně null/undefined (a vrátí undefined v tom případě), ale to platí pouze pro user . Další nemovitosti jsou přístupné běžným způsobem. Pokud chceme, aby některé z nich byly volitelné, budeme muset nahradit více . s ?. .

Nepoužívejte nadměrně volitelné řetězení

Měli bychom použít ?. pouze tam, kde je v pořádku, že něco neexistuje.

Například pokud podle naší kódové logiky user objekt musí existovat, ale address je nepovinné, pak bychom měli napsat user.address?.street , ale ne user?.address?.street .

Pak, pokud user je nedefinovaný, uvidíme v něm chybu programování a opravíme ji. V opačném případě, pokud nadměrně použijeme ?. , chyby v kódování mohou být umlčeny tam, kde to není vhodné, a jejich ladění je obtížnější.

Proměnná před ?. musí být deklarováno

Pokud neexistuje proměnná user vůbec, pak user?.anything spustí chybu:

// ReferenceError: user is not defined
user?.address;

Proměnná musí být deklarována (např. let/const/var user nebo jako parametr funkce). Volitelné řetězení funguje pouze pro deklarované proměnné.

Zkrat

Jak již bylo řečeno, ?. okamžitě zastaví („zkratuje“) vyhodnocování, pokud levá část neexistuje.

Pokud tedy existují další volání funkcí nebo operace napravo od ?. , nebudou vyrobeny.

Například:

let user = null;
let x = 0;

user?.sayHi(x++); // no "user", so the execution doesn't reach sayHi call and x++

alert(x); // 0, value not incremented

Další varianty:?.(), ?.[]

Volitelné řetězení ?. není operátor, ale speciální syntaktická konstrukce, která také pracuje 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 mají někteří naši uživatelé admin a někteří ne:

let userAdmin = {
 admin() {
 alert("I am admin");
 }
};

let userGuest = {};

userAdmin.admin?.(); // I am admin

userGuest.admin?.(); // nothing happens (no such method)

Zde v obou řádcích nejprve použijeme tečku (userAdmin.admin ), abyste získali admin vlastnost, protože předpokládáme, že user objekt existuje, takže jej lze bezpečně číst.

Potom ?.() zkontroluje levou část:pokud admin funkce existuje, pak se spustí (to platí pro userAdmin ). Jinak (pro userGuest ) vyhodnocení se zastaví bez chyb.

?.[] syntaxe také funguje, pokud bychom chtěli použít hranaté závorky [] pro přístup k vlastnostem místo tečky . . Podobně jako v předchozích případech umožňuje bezpečně číst vlastnost z objektu, který nemusí existovat.

let key = "firstName";

let user1 = {
 firstName: "John"
};

let user2 = null;

alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined

Také můžeme použít ?. s delete :

delete user?.name; // delete user.name if user exists
Můžeme použít ?. pro bezpečné čtení a mazání, ale ne pro zápis

Volitelné řetězení ?. nemá na levé straně úkolu žádné využití.

Například:

let user = null;

user?.name = "John"; // Error, doesn't work
// because it evaluates to: undefined = "John"

Shrnutí

Volitelné řetězení ?. syntaxe má tři formy:

  1. obj?.prop – vrátí obj.prop pokud obj existuje, jinak undefined .
  2. obj?.[prop] – vrátí obj[prop] pokud obj existuje, jinak undefined .
  3. obj.method?.() – zavolá obj.method() pokud obj.method existuje, jinak vrátí undefined .

Jak vidíme, všechny jsou přímočaré a snadno použitelné. ?. zkontroluje levou část pro null/undefined a umožňuje, aby hodnocení pokračovalo, pokud tomu tak není.

Řetězec ?. umožňuje bezpečný přístup k vnořeným vlastnostem.

Přesto bychom měli použít ?. opatrně, pouze tam, kde je podle naší logiky kódu přijatelné, že levá část neexistuje. Aby před námi neskrýval chyby v programování, pokud k nim dojde.