Ż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 i Infinity to typ number
  • 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