7 wskazówek dotyczących obsługi niezdefiniowanych w JavaScript

Większość współczesnych języków, takich jak Ruby, Python czy Java, ma jedną wartość null (nil lub null), co wydaje się rozsądnym podejściem.

Ale JavaScript jest inny.

null, ale także undefined, reprezentują w JavaScript puste wartości. Jaka jest więc dokładna różnica między nimi?

Krótka odpowiedź jest taka, że interpreter JavaScript zwraca undefined podczas uzyskiwania dostępu do zmiennej lub właściwości obiektu, która nie została jeszcze zainicjowana. Na przykład:

Z drugiej strony null reprezentuje brakujący obiekt odniesienie. JavaScript nie inicjalizuje zmiennych ani właściwości obiektów za pomocą null.

Niektóre metody natywne, takie jak String.prototype.match(), mogą zwracać null w celu oznaczenia brakującego obiektu. Spójrz na przykład:

Ponieważ JavaScript jest przyzwalający, programiści mają pokusę dostępu do niezainicjowanych wartości. Ja też jestem winny takiej złej praktyki.

Często takie ryzykowne działania generują undefined powiązane błędy:

  • TypeError: "undefined" is not a function
  • TypeError: Cannot read property "<prop-name>" of undefined
  • i podobne błędy pisarskie.

Programista JavaScript może zrozumieć ironię tego żartu :

Aby zredukować takie błędy, musisz zrozumieć przypadki, w których undefined jest generowany. Zbadajmy undefined i jego wpływ na bezpieczeństwo kodu.

1. Co jest niezdefiniowane

JavaScript ma 6 typów pierwotnych:

I oddzielny typ obiektu: {name: "Dmitri"} , .

Z 6 typów pierwotnych undefined jest wartością specjalną z własnym typem Niezdefiniowanym. Zgodnie ze specyfikacją ECMAScript:

Wartość pierwotna niezdefiniowanej wartości jest używana, gdy zmiennej nie przypisano wartości.

Norma jasno określa, że undefined otrzymasz podczas uzyskiwania dostępu do niezainicjowanych zmiennych, nieistniejących właściwości obiektów, nieistniejące elementy tablicy i tym podobne.

Kilka przykładów:

Powyższy przykład pokazuje, że dostęp do:

  • niezainicjowanej zmiennej number
  • nieistniejąca właściwość obiektu movie.year
  • lub nieistniejący element tablicy movies

są oceniane jako undefined.

Specyfikacja ECMAScript definiuje typ undefined wartości:

Niezdefiniowany typ to typ, którego jedyną wartością jest undefined wartość.

W tym sensie zwraca ciąg "undefined" dla wartości undefined:

Oczywiście typeof dobrze sprawdza się w celu sprawdzenia, czy zmienna zawiera wartość undefined:

2. Scenariusze powodujące tworzenie niezdefiniowanych

2.1 Niezainicjowana zmienna

Zadeklarowana zmienna, ale nie została jeszcze przypisana wartość (niezainicjowana), jest domyślnie undefined.

Zwykłe i proste:

myVariable został zadeklarowany i nie ma jeszcze przypisanej wartości. Dostęp do zmiennej daje undefined.

Efektywnym podejściem do rozwiązywania problemów związanych z niezainicjowanymi zmiennymi jest, gdy tylko jest to możliwe, przypisanie wartości początkowej. Im mniej zmienna istnieje w niezainicjowanym stanie, tym lepiej.

Idealnie byłoby przypisać wartość zaraz po zadeklarowaniu const myVariable = "Initial value". Ale to nie zawsze jest możliwe.

Wskazówka 1: Faworyzuj const, w przeciwnym razie użyj let, ale pożegnaj się z var

Moim zdaniem jedną z najlepszych cech ECMAScript 2015 jest nowy sposób deklarowania zmiennych przy użyciu const i let. To duży krok naprzód.

const i let mają zasięg blokowy (w przeciwieństwie do starszej funkcji o zakresie var) i istnieją w czasowej martwej strefie aż do wiersza deklaracji.

Polecam zmienną const, gdy jej wartość nie ulegnie zmianie. Tworzy niezmienne powiązanie.

Jedną z fajnych cech const jest to, że musisz przypisać wartość początkową do zmiennej const myVariable = "initial". Zmienna nie jest wystawiona na stan niezainicjowany i dostęp do undefined jest niemożliwy.

Sprawdźmy funkcję sprawdzającą, czy słowo jest palindromem:

length i half zmiennym przypisuje się wartość raz. Wydaje się rozsądne zadeklarowanie ich jako const, ponieważ te zmienne się nie zmienią.

Użyj deklaracji let dla zmiennych, których wartość może się zmieniać. Jeśli to możliwe, od razu przypisz wartość początkową, np. let index = 0.

A co ze starą szkołą var? Proponuję przestać go używać.

var Problem z deklaracją polega na podnoszeniu zmiennej w zakresie funkcji. Możesz zadeklarować zmienną var gdzieś na końcu zakresu funkcji, ale nadal możesz uzyskać do niej dostęp przed deklaracją: i otrzymasz undefined.

myVariable jest dostępny i zawiera undefined nawet przed wierszem deklaracji: var myVariable = "Initial value".

W przeciwieństwie do zmiennej const lub let nie można uzyskać dostępu przed wierszem deklaracji – zmienna znajduje się w czasowej martwej strefie przed deklaracją. I to jest fajne, ponieważ masz mniejsze szanse na dostęp do undefined.

Powyższy przykład zaktualizowany o let (zamiast of var) generuje ReferenceError, ponieważ zmienna w czasowej martwej strefie jest niedostępna.

Zachęcanie do używania const w przypadku niezmiennych powiązań lub let w inny sposób zapewnia praktykę, która zmniejsza widoczność niezainicjowanych zmienna.

Wskazówka 2: Zwiększ spójność

Spójność charakteryzuje stopień, w jakim elementy modułu (przestrzeń nazw, klasa, metoda, blok kodu) należą do siebie. Spójność może być wysoka lub niska.

Moduł o wysokiej spójności jest preferowany, ponieważ elementy takiego modułu koncentrują się wyłącznie na jednym zadaniu. To sprawia, że moduł:

  • Skoncentrowany i zrozumiały: łatwiejszy do zrozumienia, co robi moduł
  • Konserwowalny i łatwiejszy do refaktoryzacji: zmiana w module wpływa na mniejszą liczbę modułów
  • Wielokrotnego użytku: skupienie się na jednym zadaniu sprawia, że moduł jest łatwiejszy do ponownego wykorzystania
  • Testowalny: łatwiej byłoby przetestować moduł skoncentrowany na jednym zadaniu

Cechą dobrze zaprojektowanego systemu jest wysoka spójność, której towarzyszy luźne sprzężenie.

Blok kodu można uznać za mały moduł. Aby skorzystać z zalet wysokiej spójności, należy utrzymywać zmienne jak najbliżej bloku kodu, który ich używa.

Na przykład, jeśli zmienna istnieje wyłącznie po to, aby utworzyć logikę zakresu blokowego, zadeklaruj i uaktywnij zmienną tylko w tym bloku (używając const lub let deklaracje). Nie ujawniaj tej zmiennej zewnętrznemu zakresowi bloku, ponieważ zewnętrzny blok nie powinien przejmować się tą zmienną.

Klasycznym przykładem niepotrzebnie wydłużonego czasu życia zmiennych jest użycie for cykl wewnątrz funkcji:

index, item i zmienne są deklarowane na początku treści funkcji. Jednak są one używane tylko pod koniec. Jaki jest problem z tym podejściem?

Między deklaracją u góry a użyciem w instrukcji for zmienne index, item są niezainicjowane i widoczne dla undefined. Mają nieracjonalnie długi cykl życia w całym zakresie funkcji.

Lepszym podejściem jest przeniesienie tych zmiennych jak najbliżej miejsca ich użycia:

index i istnieją tylko w zakresie blokowym instrukcji for. Nie mają one żadnego znaczenia poza for.
Zmienna length jest również zadeklarowana blisko źródła jej użycia.

Dlaczego zmodyfikowana wersja jest lepsza od początkowej? Zobaczmy:

  • Zmienne nie są narażone na stan niezainicjowania, więc nie ma ryzyka dostępu do undefined
  • Przenoszenie zmienne jak najbliżej miejsca ich użycia zwiększają czytelność kodu
  • Wysoko spójne fragmenty kodu są łatwiejsze do refaktoryzacji i wyodrębniania do oddzielnych funkcji, jeśli to konieczne

2.2 Dostęp nieistniejąca właściwość

Podczas uzyskiwania dostępu do nieistniejącej właściwości obiektu JavaScript zwraca undefined.

Pokażmy to na przykładzie:

favoriteMovie to obiekt z jedną właściwością title. Dostęp do nieistniejącej właściwości actors przy użyciu metody dostępu do właściwości favoriteMovie.actors daje wynik undefined.

Uzyskanie dostępu do nieistniejącej właściwości nie powoduje błędu. Problem pojawia się podczas próby pobrania danych z nieistniejącej właściwości, która jest najczęstszą pułapką undefined, odzwierciedloną w dobrze znanym komunikacie o błędzie TypeError: Cannot read property <prop> of undefined.

Nieznacznie zmodyfikujmy poprzedni fragment kodu, aby zilustrować TypeError rzut:

favoriteMovie nie ma właściwości actors, więc favoriteMovie.actors zwraca undefined.

W rezultacie uzyskanie dostępu do pierwszego elementu wartości undefined przy użyciu wyrażenia favoriteMovie.actors powoduje wyświetlenie TypeError.

Pozwalający charakter JavaScript, który umożliwia dostęp do nieistniejących właściwości, jest źródłem niedeterminizmu: właściwość może być ustawiona lub nie. Dobrym sposobem na obejście tego problemu jest ograniczenie obiektu tak, aby zawsze miał zdefiniowane właściwości, które posiada.

Niestety, często nie masz kontroli nad obiektami. Takie obiekty mogą mieć inny zestaw właściwości w różnych scenariuszach. Musisz więc obsługiwać wszystkie te scenariusze ręcznie.

Zaimplementujmy funkcję append(array, toAppend), która dodaje na początku i / lub na końcu tablicy nowych elementów. Parametr toAppend akceptuje obiekt z właściwościami:

  • first: element wstawiony na początku array
  • last: element wstawiony na końcu array.

Funkcja zwraca nową instancję tablicy, bez zmiany oryginalnej tablicy.

Pierwsza wersja append(), nieco naiwna, może wyglądać tak:

Ponieważ toAppend może pomijać właściwości first lub last, obowiązkowe jest sprawdzenie, czy te właściwości istnieją w toAppend.

Metoda dostępu do właściwości zwraca wartość undefined, jeśli właściwość nie istnieje. Pierwszą pokusą sprawdzenia, czy właściwości first lub last są obecne, jest sprawdzenie ich pod kątem undefined. Odbywa się to w poleceniach warunkowych if(toAppend.first){} i if(toAppend.last){}

Nie tak szybko. Takie podejście ma wadę. undefined, a także false, null, 0, NaN i "" to wartości fałszywe.

W obecnej implementacji append() funkcja nie pozwala na wstawianie fałszywych elementów:

Poniższe wskazówki wyjaśniają, jak poprawnie sprawdzić istnienie nieruchomości.

Wskazówka 3: Sprawdź istnienie nieruchomości

Na szczęście JavaScript oferuje kilka sposobów określenia, czy obiekt ma określoną właściwość:

  • obj.prop !== undefined: porównaj z undefined bezpośrednio
  • typeof obj.prop !== "undefined": sprawdź typ wartości właściwości
  • obj.hasOwnProperty("prop"): sprawdź, czy obiekt ma własną właściwość
  • "prop" in obj: sprawdź, czy obiekt ma własną czy dziedziczoną właściwość

Zalecam użyj operatora in. Ma krótką i słodką składnię. Obecność operatora in sugeruje wyraźny zamiar sprawdzenia, czy obiekt ma określoną właściwość, bez uzyskiwania dostępu do rzeczywistej wartości właściwości.

obj.hasOwnProperty("prop") to również fajne rozwiązanie. Jest nieco dłuższy niż operator in i weryfikuje tylko we własnych właściwościach obiektu.

Poprawmy funkcję append(array, toAppend) za pomocą operatora in:

"first" in toAppend (i "last" in toAppend) to true czy odpowiednia właściwość istnieje, false inaczej.

in rozwiązuje problem z wstawianiem fałszywych elementów 0 i false. Teraz dodanie tych elementów na początku i na końcu daje oczekiwany wynik .

Wskazówka 4: Destrukturyzacja w celu uzyskania dostępu do właściwości obiektu

Podczas uzyskiwania dostępu do właściwości obiektu czasami konieczne jest ustawienie wartości domyślnej, jeśli właściwość nie istnieje.

Aby to osiągnąć, możesz użyć in wraz z operatorem trójskładnikowym:

Składnia operatora trójskładnikowego staje się zniechęcająca, gdy wzrasta liczba właściwości do sprawdzenia. Dla każdej właściwości musisz utworzyć nowy wiersz kodu obsługujący wartości domyślne, zwiększając brzydką ścianę podobnie wyglądających operatorów trójskładnikowych.

Aby zastosować bardziej eleganckie podejście, zapoznajmy się ze świetną funkcją ES2015 zwaną destrukcją obiektów.

Destrukturyzacja obiektów umożliwia wyodrębnianie wartości właściwości obiektu bezpośrednio do zmiennych i ustawienie wartości domyślnej, jeśli właściwość nie istnieje. Wygodna składnia pozwalająca uniknąć bezpośredniego zajmowania się undefined.

Rzeczywiście, wyodrębnianie właściwości jest teraz precyzyjne:

Aby zobaczyć rzeczy w akcji, zdefiniujmy użyteczną funkcję, która zawija ciąg w cudzysłów.

quote(subject, config) akceptuje pierwszy argument jako łańcuch do zawijania. Drugi argument config to obiekt z właściwościami:

Stosując zalety destrukturyzacji obiektów, zaimplementujmy quote():

const { char = """, skipIfQuoted = true } = config rozkładanie przypisania w jednym wierszu wyodrębnia właściwości char i skipIfQuoted z obiektu config.
Jeśli w obiekcie config brakuje niektórych właściwości, przypisanie destrukturyzacji ustawia wartości domyślne : """ for char i false dla skipIfQuoted.

Na szczęście ta funkcja wciąż wymaga ulepszeń.

Przenieśmy przypisanie destrukturyzujące do sekcji parametrów. I ustaw wartość domyślną (pusty obiekt { }) dla parametru config, aby pominąć drugi argument, gdy ustawienia domyślne są wystarczające.

Przypisanie destrukturyzujące zastępuje parametr config w sygnaturze funkcji. Podoba mi się: quote() skraca się o jedną linię.

= {} po prawej stronie przypisania destrukturyzującego zapewnia, że zostanie użyty pusty obiekt, jeśli drugi argument nie zostanie w ogóle określony quote("Sunny day").

Destrukturyzacja obiektów to potężna funkcja, która wydajnie obsługuje wyodrębnianie właściwości z obiektów. Podoba mi się możliwość określenia wartości domyślnej, która ma zostać zwrócona, gdy dostępna właściwość nie istnieje. W rezultacie unikniesz undefined i kłopotów z nim związanych.

Wskazówka 5: Wypełnij obiekt właściwościami domyślnymi

Jeśli nie ma potrzeby tworzenia zmiennych dla każdej właściwości, jak to ma miejsce w przypadku przypisania destrukturyzującego, obiekt, który pomija niektóre właściwości, może zostać wypełniony z wartościami domyślnymi.

ES2015 Object.assign(target, source1, source2, ...) kopiuje wartości wszystkich wyliczalnych własnych właściwości z jednego lub więcej obiektów źródłowych do obiektu docelowego. Funkcja zwraca obiekt docelowy.

Na przykład musisz uzyskać dostęp do właściwości obiektu unsafeOptions, który nie zawsze zawiera pełny zestaw właściwości.

Aby uniknąć undefined podczas uzyskiwania dostępu do nieistniejącej właściwości z unsafeOptions, wprowadźmy pewne poprawki:

  • Zdefiniuj obiekt defaults, który przechowuje domyślne wartości właściwości
  • Wywołaj Object.assign({ }, defaults, unsafeOptions), aby zbudować nowy obiekt options. Nowy obiekt otrzymuje wszystkie właściwości z unsafeOptions, ale brakujące są pobierane z defaults.

unsafeOptions zawiera tylko właściwość fontSize. Obiekt defaults definiuje domyślne wartości właściwości fontSize i color.

Object.assign() przyjmuje pierwszy argument jako obiekt docelowy {}. Obiekt docelowy otrzymuje wartość właściwości fontSize z obiektu źródłowego unsafeOptions. A wartość właściwości color z obiektu źródłowego defaults, ponieważ unsafeOptions nie zawiera color.

Kolejność, w jakiej wyliczane są obiekty źródłowe, ma znaczenie: późniejsze właściwości obiektu źródłowego nadpisują wcześniejsze.

Możesz teraz bezpiecznie uzyskać dostęp do dowolnej właściwości obiektu options, w tym options.color, która nie była dostępna w unsafeOptions początkowo.

Na szczęście istnieje łatwiejsza alternatywa wypełnienia obiektu właściwościami domyślnymi.Zalecam użycie właściwości rozłożenia w inicjatorach obiektów.

Zamiast wywołania Object.assign(), użyj składni rozszerzania obiektów, aby skopiować do obiektu docelowego wszystkie własne i wyliczalne właściwości z obiektów źródłowych:

inicjator obiektu rozprzestrzenia właściwości z obiektów źródłowych defaults i unsafeOptions. Kolejność określania obiektów źródłowych jest ważna: późniejsze właściwości obiektu źródłowego zastępują wcześniejsze.

Wypełnianie niekompletnego obiektu domyślnymi wartościami właściwości to skuteczna strategia zapewniająca bezpieczeństwo i trwałość kodu. Niezależnie od sytuacji obiekt zawsze zawiera pełny zestaw właściwości: i nie można wygenerować undefined.

Dodatkowa wskazówka: łączenie zerowe

Operator łączenie zerowe zwraca wartość domyślną, gdy jego operandem jest undefined lub null:

Zerowy operator łączenia jest wygodny w dostępie do właściwości obiektu, mając wartość domyślną, gdy ta właściwość to undefined lub null:

styles obiekt nie ma właściwości color, więc styles.color akcesor właściwości to undefined. styles.color ?? "black" zwraca wartość domyślną "black".

styles.fontSize to 18, więc zerowy operator łączenia zwraca wartość właściwości 18.

2.3 Parametry funkcji

Parametry funkcji domyślnie mają wartość undefined.

Zwykle funkcja zdefiniowana z określoną liczbą parametrów powinna być wywoływana z taką samą liczbą argumentów. Wtedy parametry uzyskują oczekiwane wartości:

Kiedy multiply(5, 3), parametry a i b otrzymują 5 i odpowiednio 3 wartości. Mnożenie jest obliczane zgodnie z oczekiwaniami: 5 * 3 = 15.

Co się dzieje, gdy pominiesz argument przy wywołaniu? Odpowiedni parametr wewnątrz funkcji to undefined.

Zmodyfikujmy nieco poprzedni przykład, wywołując funkcję z jednym argumentem:

Wywołanie multiply(5) jest wykonywane z jednym argumentem: w rezultacie parametr a to 5, ale Parametr b to undefined.

Porada 6: Użyj domyślnej wartości parametru

Czasami funkcja nie wymaga pełnego zestawu argumentów przy wywołaniu. Możesz ustawić wartości domyślne dla parametrów, które nie mają wartości.

Przypominając poprzedni przykład, dokonajmy ulepszeń. Jeśli parametr b to undefined, ustaw go domyślnie na 2:

Funkcja jest wywoływana z jednym argumentem multiply(5). Początkowo parametr a to 2, a b to undefined.
Instrukcja warunkowa sprawdza, czy b to undefined. Jeśli tak się stanie, przypisanie b = 2 ustawia wartość domyślną.

Chociaż przedstawiony sposób przypisywania wartości domyślnych działa, nie polecam bezpośredniego porównywania z undefined. Jest rozwlekły i wygląda jak włamanie.

Lepszym podejściem jest użycie funkcji parametrów domyślnych ES2015. Jest krótkie, wyraziste i nie ma bezpośrednich porównań z undefined.

Dodanie wartości domyślnej do parametru b = 2 wygląda lepiej:

b = 2 w sygnaturze funkcji zapewnia, że jeśli b to undefined, parametr domyślnie wynosi 2.

Domyślne parametry ES2015 są intuicyjne i wyraziste. Zawsze używaj go do ustawiania wartości domyślnych parametrów opcjonalnych.

2.4 Wartość zwracana przez funkcję

Niejawnie, bez instrukcji return, funkcja JavaScript zwraca undefined.

Funkcja, która nie ma return niejawnie zwraca undefined:

nie zwraca żadnych wyników obliczeń. Wynik wywołania funkcji to undefined.

Taka sama sytuacja ma miejsce, gdy występuje instrukcja return, ale w pobliżu nie ma wyrażenia:

return; jest wykonywana, ale nie zwraca żadnego wyrażenia. Wynik wywołania to również undefined.

Oczywiście wskazanie w pobliżu return zwracanego wyrażenia działa zgodnie z oczekiwaniami:

Teraz wywołanie funkcji jest oceniane jako 4, czyli 2 do kwadratu.

Wskazówka 7: Nie ufaj automatycznemu wstawianiu średnika

Poniższa lista instrukcji w JavaScript musi kończyć się średnikami (;) :

  • pusta instrukcja
  • let, const, var, import, export deklaracje
  • wyrażenie wyrażenia
  • debugger instrukcja
  • continue instrukcja, break instrukcja
  • throw oświadczenie
  • return instrukcja

Jeśli używasz jednego z powyższych stwierdzeń, pamiętaj, aby na końcu umieścić średnik:

Na końcu obu let deklaracja i return zapis obowiązkowy średnik.

Co się stanie, jeśli nie chcesz ich podawać średniki? W takiej sytuacji ECMAScript zapewnia mechanizm automatycznego wstawiania średników (ASI), który wstawia brakujące średniki.

Z pomocą ASI możesz usunąć średniki z poprzedniego przykładu:

Powyższy tekst to prawidłowy kod JavaScript. Brakujące średniki są wstawiane automatycznie.

Na pierwszy rzut oka wygląda to całkiem obiecująco. Mechanizm ASI pozwala na pominięcie niepotrzebnych średników. Możesz zmniejszyć rozmiar kodu JavaScript i ułatwić jego czytanie.

Jest jedna mała, ale irytująca pułapka stworzona przez ASI. Gdy nowa linia znajduje się między return a zwróconym wyrażeniem return \n expression, ASI automatycznie wstawia średnik przed nową linią return; \n expression.

Co to znaczy mieć instrukcję return; wewnątrz funkcji? Funkcja zwraca undefined. Jeśli nie znasz szczegółowo mechanizmu ASI, nieoczekiwanie zwrócony undefined jest mylący.

Na przykład przestudiujmy zwróconą wartość wywołania getPrimeNumbers():

Pomiędzy instrukcją return a wyrażeniem literału tablicowego istnieje nowa linia. JavaScript automatycznie wstawia średnik po return, interpretując kod w następujący sposób:

Instrukcja return; sprawia, że funkcja getPrimeNumbers() zwraca undefined zamiast oczekiwanej tablicy.

Problem został rozwiązany poprzez usunięcie nowej linii między return a literałem tablicy:

Zalecam zbadanie, jak dokładnie działa automatyczne wstawianie średnika, aby uniknąć takich sytuacji.

Oczywiście nigdy nie umieszczaj nowej linii między return a zwróconym wyrażeniem.

2,5 operator void

void <expression> ocenia wyrażenie i zwraca undefined bez względu na wynik oceny.

Jednym z przypadków użycia operatora void jest pominięcie oceny wyrażenia do undefined, opierając się na jakimś efekcie ubocznym oceny.

3. undefined w tablicach

Otrzymujesz undefined podczas uzyskiwania dostępu do elementu tablicy z indeksem spoza granic.

colors tablica ma 3 elementy, więc prawidłowe indeksy to 0, 1 i 2.

Ponieważ w indeksach 5 i -1 nie ma elementów tablicy, akcesory colors i colors to undefined.

W JavaScript możesz napotkać tak zwane tablice rzadkie. Tezy to tablice, które mają luki, tj. W niektórych indeksach nie ma zdefiniowanych elementów.

Kiedy w rzadkiej tablicy uzyskuje się dostęp do luki (czyli pustego miejsca), otrzymujemy również undefined.

Poniższy przykład generuje rzadkie tablice i próbuje uzyskać dostęp do ich pustych gniazd:

sparse1 jest tworzony przez wywołanie Array z pierwszym argumentem numerycznym.Posiada 3 puste miejsca.

sparse2 jest tworzony za pomocą literału tablicowego z brakującym drugim elementem.

W każdej z tych rzadkich tablic dostęp do pustego gniazda daje undefined.

Podczas pracy z tablicami, aby uniknąć undefined, upewnij się, że używasz prawidłowych indeksów tablic i zapobiegaj tworzeniu rzadkich tablic.

4. Różnica między wartościami undefined i null

Jaka jest główna różnica między undefined a null? Obie wartości specjalne implikują stan pusty.

undefined reprezentuje wartość zmiennej, która nie została jeszcze zainicjowana, natomiast null reprezentuje celową nieobecność obiektu.

Zbadajmy różnicę w kilku przykładach.

Zmienna number jest zdefiniowana jednak nie ma przypisanej wartości początkowej:

number zmienna to undefined, co oznacza niezainicjowaną zmienną.

Ta sama niezainicjowana koncepcja ma miejsce, gdy uzyskuje się dostęp do nieistniejącej właściwości obiektu:

Ponieważ lastName właściwość nie istnieje w obj, JavaScript ocenia obj.lastName na undefined.

Z drugiej strony wiesz, że zmienna oczekuje obiektu. Ale z jakiegoś powodu nie możesz utworzyć wystąpienia obiektu. W takim przypadku null jest znaczącym wskaźnikiem brakującego obiektu.

Na przykład clone() to funkcja, która klonuje zwykły obiekt JavaScript. Funkcja powinna zwrócić obiekt:

Jednak clone() może zostać wywołane z argumentem niebędącym przedmiotem: 15 lub null. W takim przypadku funkcja nie może utworzyć klonu, więc zwraca null – wskaźnik brakującego obiektu.

typeof operator rozróżnia undefined i null:

Również operator ścisłej jakości === poprawnie rozróżnia undefined from null:

5. Podsumowanie

undefined istnienie jest konsekwencją permisywnego charakteru JavaScript, który pozwala na użycie:

  • niezainicjowanych zmiennych
  • nieistniejące właściwości lub metody obiektu
  • indeksy poza granicami dostępu do elementów tablicy
  • wynik wywołania funkcji, która nic nie zwraca

Bezpośrednie porównywanie z undefined jest niebezpieczne, ponieważ polegasz na dozwolonej, ale odradzanej praktyce wspomnianej powyżej.

Skuteczną strategią jest ograniczenie co najmniej występowania w kodzie słowa kluczowego undefined poprzez stosowanie dobrych nawyków, takich jak:

  • zmniejszyć użycie niezainicjowanych zmiennych
  • skrócić cykl życia zmiennych i zbliżyć je do źródła ich użycia
  • w miarę możliwości przypisać zmiennym wartości początkowe
  • faworyzować const, w przeciwnym razie użyj let
  • użyj wartości domyślnych dla nieistotnych parametrów funkcji
  • sprawdź właściwości istnienie lub wypełnienie niebezpiecznych obiektów domyślnymi właściwościami
  • unikaj stosowania rzadkich tablic

Czy to dobrze, że JavaScript ma zarówno undefined i null do reprezentowania pustych wartości?

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *