Jednym z aspektów programowania JavaScript, z którym boryka się wielu programistów, jest używanie wartości opcjonalnych. Jakie są najlepsze strategie minimalizowania błędów spowodowanych przez wartości, które mogą być null
, undefined
lub w inny sposób niezainicjowane w czasie wykonywania?
Niektóre języki mają wbudowane afordancje dla takich okoliczności. W niektórych językach wpisywanych statycznie możesz powiedzieć, że null
i undefined
są niedozwolonymi wartościami i pozwól swojemu językowi programowania zgłosić błąd TypeError podczas kompilacji , ale nawet w tych językach, które nie mogą zapobiec napływaniu zerowych danych wejściowych do programu w czasie wykonywania.
Aby lepiej rozwiązać ten problem, musimy zrozumieć, skąd te wartości mogą pochodzić. Tutaj to jedne z najczęstszych źródeł:
- Wprowadzane przez użytkownika
- Rekordy bazy danych / sieci
- Stan niezainicjowany
- Funkcje, które mogą nic nie zwracają
Kiedy masz do czynienia z danymi wejściowymi użytkownika, walidacja jest pierwszą i najlepszą linią obrony. Często polegam na walidatorach schematów, aby pomóc w tym zadaniu. Na przykład sprawdź -jsonschema-form.
Hydrating Records from Input
Zawsze przekazuję dane wejściowe, które otrzymuję z sieci, bazy danych lub użytkownika poprzez funkcję nawadniania. Na przykład użyję twórcy akcji redux, którzy mogą obsługiwać
wartości do nawodnienia rekordów użytkowników:
const setUser = ({ name = "Anonymous", avatar = "anon.png" } = {}) => ({
type: setUser.type,
payload: {
name,
avatar
}
});
setUser.type = "userReducer/setUser";
Czasami musisz wyświetlić różne rzeczy w zależności od aktualnego stanu danych. Jeśli możliwe jest wyświetlenie strony przed zainicjowaniem wszystkich danych, możesz znaleźć się w takiej sytuacji. Na przykład, gdy wyświetlasz salda pieniężne użytkownikowi, możesz przypadkowo wyświetlić saldo 0 USD przed załadowaniem danych. Widziałem to zdenerwowanych użytkowników wiele razy. Możesz tworzyć niestandardowe typy danych, które generują różne wyniki w oparciu o bieżący stan:
Powyższy kod jest maszyną stanów, która uniemożliwia wyświetlanie nieprawidłowych stanów. Kiedy po raz pierwszy utworzysz saldo, zostanie ustawione w stanie uninitialized
. Jeśli spróbujesz wyświetlić saldo, gdy stan jest uninitialized
, zamiast tego zawsze otrzymasz wartość zastępczą („--
„).
Aby to zmienić, musisz jawnie ustawić wartość, wywołując metodę .set
lub skrót setBalance
zdefiniowaliśmy poniżej fabrykę createBalance
.
Sam stan jest hermetyzowany, aby chronić go przed zewnętrznymi zakłóceniami, aby upewnić się, że inne funkcje nie mogą go pobrać i ustawić do nieprawidłowego stanu.
Uwaga: jeśli zastanawiasz się, dlaczego używamy do tego ciągów znaków zamiast liczb, to dlatego, że reprezentuję typy pieniędzy ciągi dużych liczb z dużą dokładnością dziesiętną, aby uniknąć błędów zaokrągleń i dokładnie reprezentują wartości transakcji kryptowalutowych, które mogą mieć dowolną znaczącą dokładność dziesiętną.
Jeśli Korzystając z architektury Redux lub Redux, możesz deklarować maszyny stanu za pomocą Redu x-DSM.
Unikaj tworzenia wartości null i niezdefiniowanych
We własnych funkcjach możesz uniknąć tworzenia null
lub , aby rozpocząć. Istnieje kilka sposobów na zrobienie tego wbudowanego w JavaScript, który przychodzi mi do głowy. Zobacz poniżej.
Unikaj null
Nigdy nie tworzę jawnie wartości null
w JavaScript, ponieważ nigdy nie widziałem sensu posiadania dwóch różnych prymitywne wartości, które zasadniczo oznaczają „ta wartość nie istnieje”.
Od 2015 roku JavaScript obsługuje wartości domyślne, które są wypełniane, gdy nie podasz wartości dla danego argumentu lub właściwości. Te ustawienia domyślne nie działają dla wartości null
. Z mojego doświadczenia wynika, że zazwyczaj jest to błąd. Aby uniknąć tej pułapki, nie używaj null
w JavaScript.
Jeśli potrzebujesz specjalnych przypadków dla niezainicjowanych lub pustych wartości, lepszym rozwiązaniem są maszyny stanowe. Zobacz powyżej.
Nowe funkcje JavaScript
Jest kilka funkcji, które mogą pomóc w radzeniu sobie z null
lub undefined
wartości. W chwili pisania tego tekstu obie są propozycjami etapu 3, ale jeśli czytasz z przyszłości, być może będziesz w stanie z nich skorzystać.
W chwili pisania tego tekstu opcjonalne łączenie łańcuchowe jest propozycją etapu 3. Działa to w ten sposób:
const foo = {};
// console.log(foo.bar.baz); // throws error
console.log(foo.bar?.baz) // undefined
Zerowy operator koalescencyjny
Również propozycja etapu 3 do dodania do specyfikacji, „zerowy operator koalescencji ”Jest po prostu fantazyjnym sposobem powiedzenia„ operator wartości rezerwowej ”. Jeśli wartość po lewej stronie to undefined
lub null
, zwraca wartość po prawej stronie.Działa to w ten sposób:
Asynchronicznie albo z obietnicami
Jeśli funkcja może nie zwrócić z wartością, dobrym pomysłem może być zawinięcie jej w obie. W programowaniu funkcjonalnym monada Albo to specjalny abstrakcyjny typ danych, który umożliwia dołączenie dwóch różnych ścieżek kodu: ścieżki sukcesu lub ścieżki niepowodzenia. JavaScript ma wbudowany asynchroniczny typ danych Monad-ish o nazwie Promise. Możesz go użyć do wykonania deklaratywnego rozgałęziania błędów dla niezdefiniowanych wartości:
Możesz napisać synchroniczną wersję tego, jeśli chcesz, ale nie potrzebowałem tego zbytnio. Zostawię to dla ciebie jako ćwiczenie. Jeśli masz dobre podstawy w funktorach i monadach, proces będzie łatwiejszy. Jeśli brzmi to onieśmielająco, nie przejmuj się tym. Po prostu korzystaj z obietnic. Są wbudowane i działają dobrze przez większość czasu.
Tablice dla Maybesa
Tablice implementują metodę map
, która wymaga funkcja, która jest stosowana do każdego elementu tablicy. Jeśli tablica jest pusta, funkcja nigdy nie zostanie wywołana. Innymi słowy, tablice w JavaScript mogą pełnić rolę Maybesa z języków takich jak Haskell.
Co to jest Może?
A Może to specjalny abstrakcyjny typ danych, który zawiera opcjonalną wartość . Typ danych przyjmuje dwie formy:
- Tylko – Może, które zawiera wartość
- Nic – Może bez wartości
Oto istota pomysłu:
To jest tylko przykład pokazujący koncepcję. Możesz zbudować całą bibliotekę przydatnych funkcji wokół maybes, implementując inne operacje, takie jak flatMap
i flat
(np. Aby uniknąć podczas tworzenia wielu funkcji zwracających może). Ale JavaScript ma już typ danych, który implementuje wiele z tych funkcji od razu po wyjęciu z pudełka, więc zwykle sięgam po to: tablicę.
Jeśli chcesz utworzyć funkcję, która może lub może nie dać wyniku (szczególnie jeśli może być więcej niż jeden wynik), możesz mieć świetny przypadek użycia zwracania tablicy.
Uważam, że map
nie zostanie wywołany na pustej liście, co jest bardzo przydatne przy unikaniu wartości null
i undefined
, ale pamiętaj, że tablica zawiera wartości null
i undefined
, wywoła funkcję z tymi wartościami, więc jeśli funkcja, którą uruchamiasz, mogłaby wygenerować null
lub undefined
, „będziesz musiał odfiltrować je z zwracanej tablicy, jak pokazano powyżej. Może to mieć wpływ na zmianę długości kolekcja.
W Haskell jest funkcja maybe
, która (jak ) stosuje funkcję do wartości. Ale wartość jest opcjonalna i zawarta w Maybe
. Możemy użyć typu danych JavaScript „s Array
, aby zrobić w zasadzie to samo:
maybe
przyjmuje wartość zastępczą , następnie funkcja do odwzorowania na tablicę może, a następnie tablica może (tablica zawierająca jedną wartość lub nic) i zwraca wynik zastosowania funkcji do zawartości tablicy lub wartość zastępczą, jeśli tablica jest pusty.
Dla wygody zdefiniowałem również funkcję toMaybeArray
i ustawiłem funkcję maybe
, aby jest to najbardziej oczywiste w tej demonstracji.
Jeśli chciałbyś zrobić coś takiego w kodzie produkcyjnym, stworzyłem przetestowaną jednostkowo bibliotekę open source, aby to ułatwić. Nazywa się Maybearray. Zaletą Maybearray nad innymi bibliotekami JavaScript Maybe jest to, że wykorzystuje natywne tablice JavaScript do reprezentowania wartości, więc nie trzeba traktować ich w żaden specjalny sposób ani robić nic specjalnego, aby konwertować w tę iz powrotem. Kiedy napotkasz Może tablice podczas debugowania, nie musisz pytać „co to za dziwny typ ?!” To po prostu tablica wartości lub pusta tablica i widzieliście je już milion razy.
Następne kroki
Na EricElliottJS.com jest dużo więcej treści, w tym dużo filmów, ćwiczeń, nagranych screencastów i krótkich wskazówek. Jeśli nie jesteś członkiem, teraz jest świetny czas, aby zobaczyć, czego Ci brakowało!