Żeby nieco skomplikować sytuację jeśli chodzi o typ number
powiem wam jeszcze o dodatkowych wartościach jakie może ten typ posiadać. Są to wartości NaN, Infinity, -Infinity
. Gdzie NaN
tłumaczymy jako Not a Number.
Inne liczby typu Number
Gdy sprawdzimy typ tych wartości za pomocą operatora typeof
okaże się, że wymienione wartości to także typ number
.
console.log(typeof NaN); // number
console.log(typeof Infinity); // number
console.log(typeof -Infinity); // number
Niestety wprowadza to trochę zamieszania. Nie chodzi tutaj tylko o nazwę wartości NaN
, która jest określona jako Not a Number
, a przecież jest typu number
. Za chwilę zobaczymy, jakie są problemy z wartością NaN
Wartość NaN
Najpierw omówmy sobie typ NaN
, jest on bardzo ciekawy i dość problematyczny.
Przyjrzymy się najpierw sytuacjom, kiedy otrzymamy NaN
:
console.log(42 * 'test'); // NaN
console.log(42 / 'test'); // NaN
console.log(42 - 'test'); // NaN
console.log('test' * 'test'); // NaN
console.log(42 * '42'); // 1764
console.log('8' * '8'); // 64
Wykonując jakąś operację matematyczną z innym literałem niż liczba możemy otrzymać wartość NaN
. Dzieje się tak zawsze, gdy nie jest możliwe z takiej operacji zwrócenie liczby. W ostatnich przykładzie mamy jednak mnożenie z typem string
, gdzie zachodzi niejawna konwersja na typ number
i
działanie udaje się przeprowadzić.
Zwróć uwagę, że nie ma tutaj operacji dodawania. Gdy używamy znaku plus w połączeniu z typem string
znak ten nie służy wtedy do przeprowadzenia operacji matematycznej, ale do łączenia wartości. Prostym przykładem jest dodanie liczby do tekstu:
console.log(5 + 'test'); // 5test as a 'string'
console.log(5 + '100'); // 5100 as a 'string'
console.log('5' + 100); // 5100 as a 'string'
W takim przypadku otrzymujemy wartość typu string
. Jest to kolejny niuans, na który warto zwrócić uwagę. Omówimy go jednak bardziej szczegółowo przy typie string
.
Widzimy zatem, że przy próbie wykonania jakieś operacji matematycznej możemy zostać zaskoczeni wynikiem, możemy otrzymać wartość NaN
, liczbę, albo po prostu zwykły tekst.
Sprawdzenie wartości NaN
Jak już wiemy, możemy wykonać dziwną operacją matematyczną i otrzymać NaN
jako wynik. Niestety JavaScript nie zwraca nam w tym przypadku żadnego błędu, co byłoby chyba najbardziej przydatne.
Może się bowiem okazać, że zmienił się jeden z typów, na którym wykonujemy operację matematyczną i nasza aplikacja dalej działa produkując wartość NaN
, która z czasem gdzieś tam ujawni się użytkownikowi.
Przy możliwości wystąpienia takiej sytuacji, warto sprawdzić, czy wartość jest NaN
, tym bardziej, gdy nie do końca jesteśmy pewni, na jakich danych pracujemy.
Znamy już operator porównania i możemy to zrobić:
const result = 42 / 'test';
console.log(result); // NaN
console.log(result === NaN); // false
Wykonuję proste działanie, które zwróci NaN
i próbuję porównać moją wartość NaN
z wartością ogólną NaN
. Okazuje się jednak, że NaN
nie jest równe NaN
, wypiszmy to jeszcze raz do konsoli, bo trochę trudno uwierzyć:
console.log(NaN === NaN); // false
Otrzymujemy wynik false
. Dzieje się tak, ponieważ, każda wytworzona wartość NaN
jest unikalna na swój sposób. Wartość NaN
nigdy nie jest równe sobie. Ciekawostką jest to, że jest to jedyna wartość w JavaScript, która nigdy nie jest równa sobie.
Żeby sobie z tym poradzić, użyjemy gotowych metod. W JavaScript mamy zaimplementowane dwie metody do sprawdzania, czy mamy do czynienia z wartością NaN
.
Możemy wywołać metodę globalną isNaN
:
console.log(isNaN(result)) // true
lub też metodę z obiektu Number
czyli Number.isNaN
:
console.log(Number.isNaN(result)) // true
Obie metody w tym przypadku zwracają tę samą wartość true
. Wiemy więc, że wynik naszego działania jest NaN
i możemy jakoś zareagować w naszej aplikacji.
Problemy z isNaN
Niestety sama metoda isNaN
nie działa do końca zbyt dokładnie. Jej definicja mówi:
Funkcja isNaN () określa, czy wartość jest NaN, czy nie.
Nie do końca działa to precyzyjnie, gdy przekażemy tam stringa:
console.log(isNaN('test')) // true
W tym przypadku funkcja isNaN
twierdzi, że string jest NaN
. Jak wiemy string ma swój typ string
nie jest to typ number
. Więc nie może być wartością NaN
. Niestety zachodzi tam niejawna konwersja do typu number
przez co ostatecznie ze stringa powstaje NaN
. Jest to jednak błędne
działanie, bo sama funkcja miała określić czy przekazywana wartość jest NaN
.
Gdy weźmiemy natomiast metodę pochodzącą z obiektu Number
zobaczymy inny wynik:
console.log(Number.isNaN('test')) // false
Zadaniem tej metody jest dokładne określenie czy coś jest NaN
i ma typ number
. Jest to zdecydowanie bardziej precyzyjna metoda, którą warto używać.
Niestety tutaj znowu objawia się słabość języka JavaScript. Błędne implementacje z przeszłości są przykrywane lepszymi rozwiązaniami w najnowszych wersjach języka. Przez to dzisiaj mamy dwie funkcje, których nazwa mówi, że robią to samo, jednak dają nam różne wyniki.
Na koniec warto sobie jeszcze porównać różnice działania obydwu funkcji przy przekazywaniu innych wartości niż number
:
console.log(Number.isNaN(0 / 0)); // true
console.log(Number.isNaN(undefined)); // false
console.log(Number.isNaN({})); // false
console.log(Number.isNaN('blabla')); // false
console.log(isNaN(0 / 0)); // true
console.log(isNaN(undefined)); // true
console.log(isNaN({})); // true
console.log(isNaN('blabla')); // true
Widzimy różnicę, że Number.isNaN
dokładnie określa czy coś ma typ number
i jest NaN
. Wszelkie inne wartości, nie są typem number
więc nie mogą być NaN
.
Niestety funkcja isNaN
nie jest tak precyzyjna i forsuję konwersję do typu number
po czym stara się określić czy jest to wartość NaN
. Dlatego, gdy przekażemy do niej inne wartości niż number
często zwróci nam informację, że wartość ta jest NaN
. Przykładem jest choćby string
, który jest
określony przez funkcję jako NaN
, ale nie jest wartością liczbową samą w sobie.
Jeżeli byśmy chcieli sprawdzić, czy mamy do czynienia z prawdziwą liczbą, która nie jest przy okazji NaN
nasz zapis może wyglądać tak:
const value = 55 * 'test';
console.log(typeof value === 'number' && !Number.isNaN(value)) // false
W tym przypadku sprawdzamy, czy wartość jest typu number
(zauważ, że typ number
zapisany jest jako string!) oraz czy ten number
nie jest NaN
. Jest to jeden z przykładów, który można użyć do otrzymywania prawdziwych liczb.
Wartość Infinity
Inną wartością specjalną dla number
jest wartość Infinity
oraz -Infinity
. Otrzymujemy ją na przykład, gdy podzielimy liczbę przez 0. W wielu językach programowania takie działanie powoduje wyrzucenie wyjątku i otrzymania błędu. Nawet najprostszy kalkulator odmawia podzielenia przez 0.
Zobaczmy zatem, co się stanie, gdy będziemy wykonywać taką operację w JavaScript:
const x = 5 / 0;
console.log(x); // Infinity
console.log(typeof x); // number
Gdy podzielimy jakaś wartość przez zero, okaże się, że otrzymamy wartość Infinity
. Dodatkowo wartość ta ma typ number
.
Możemy także otrzymać wartość -Infinity
dzieląc liczbę przez ujemne 0, które występuje w JavaScript:
console.log(5 / -0) // -Infinity
Wartość Infinity
ma także swoją reprezentację jako stała w obiekcie Number
:
console.log(Number.POSITIVE_INFINITY) // Infinity
console.log(Number.NEGATIVE_INFINITY) // -Infinity
Na szczęście możemy porównać Infinity
samo ze sobą i otrzymać wartość true
:
console.log(Number.POSITIVE_INFINITY === Infinity); // true
Nie jest to więc to tak problematyczna wartość jak NaN
.
Sprawdzanie Infinity
Nie będziemy się nad tą wartością zbyt długo rozwodzić. Najprostszym sposobem na uchronienie się przed tą wartością jest zabezpieczenie się przed dzieleniem przez 0. Możemy stworzyć do tego funkcję, która będzie sprawdzała, czy dzielnik nie jest zerem.
Innym sposobem jest sprawdzanie konkretnej wartości za pomocą metody Number.isFinite
.
Zobaczmy przykładowe wartości:
console.log(Number.isFinite(Infinity)); // false
console.log(Number.isFinite(NaN)); // false
console.log(Number.isFinite(-Infinity)); // false
console.log(Number.isFinite(0)); // true
console.log(Number.isFinite(2e42)); // true
console.log(Number.isFinite('0')); // false
do dyspozycji mamy także globalną funkcję isFinite
, która działa podobnie jak globalna funkcja isNaN
. Najpierw próbuje konwertować wartość do liczby i potem sprawdzić, czy jest to wartość Infinity
.
console.log(isFinite(Infinity)); // false
console.log(isFinite(NaN)); // false
console.log(isFinite(-Infinity)); // false
console.log(isFinite(0)); // true
console.log(isFinite(2e42)); // true
console.log(isFinite('0')); // true
Dlatego też powstaje nam różnica, gdy próbujemy przekonwertować liczbę w postaci string
.
Jak widzimy i NaN
i Infinity
mogą być wartościami, na które trafimy pracując z typem number
. Wiemy teraz, skąd się biorą i jak z nimi pracować.
Co warto zapamiętać
- wartość
NaN
iInfinity
to typnumber
- wartość
NaN
otrzymamy, gdy wykonamy nieprawidłową operację matematyczną, najczęściej na złych typach - wartość
NaN
nie jest sama sobie równa, powstały specjalne funkcje do sprawdzania tej wartości - najlepszą opcją dzisiaj jest używanie metody
Number.isNaN
- wartość
Infinity
i-Infinity
powstaje najczęściej przy dzieleniu przez 0 - wartość tą możemy sprawdzać przez metodę
Number.isFinite