Dlaczego isNaN (null) == false w JS?


129

Ten kod w JS daje mi wyskakujące okienko z napisem „Myślę, że null to liczba”, co wydaje mi się nieco niepokojące. czego mi brakuje?

if (isNaN(null)) {
  alert("null is not a number");
} else {
  alert("i think null is a number");
}

Używam przeglądarki Firefox 3. Czy to błąd przeglądarki?

Inne testy:

console.log(null == NaN);   // false
console.log(isNaN("text")); // true
console.log(NaN == "text"); // false

Więc problem wydaje się nie polegać na dokładnym porównaniu z NaN?

Edycja: Teraz udzielono odpowiedzi na pytanie, wyczyściłem swój post, aby mieć lepszą wersję dla archiwum. Jednak powoduje to, że niektóre komentarze, a nawet odpowiedzi są trochę niezrozumiałe. Nie obwiniaj ich autorów. Zmieniłem między innymi:

  • Usunąłem notatkę mówiącą, że schrzaniłem nagłówek, przywracając jego znaczenie
  • Wcześniejsze odpowiedzi pokazały, że nie wyjaśniłem wystarczająco jasno, dlaczego uważam, że zachowanie jest dziwne, więc dodałem przykłady, które sprawdzają ciąg i wykonują ręczne porównanie.

3
nie masz na myśli "Dlaczego isNaN (null) == false"?
Matt Rogish,

Bazując na twoim kodzie, isnan (null) zwraca false (null nie oznacza „nie jest liczbą”), jeśli mówi „Myślę, że null to liczba”.
devinmoore,

Odpowiedzi:


114

Wydaje mi się, że kod próbuje zapytać „czy jest xnumeryczny?” z konkretnym przypadkiem tutaj x = null. Funkcji isNaN()można użyć, aby odpowiedzieć na to pytanie, ale semantycznie odnosi się ona konkretnie do wartości NaN. Z Wikipedii dla NaN:

NaN ( N ot a N umber) to wartość liczbowego typu danych reprezentująca wartość niezdefiniowaną lub nieprzedstawialną, szczególnie w obliczeniach zmiennoprzecinkowych.

W większości przypadków uważamy, że odpowiedź na pytanie „czy null numeric?” powinno być nie. Jednak isNaN(null) == falsejest semantycznie poprawna, ponieważ nullnie jest NaN.

Oto wyjaśnienie algorytmiczne:

Funkcja isNaN(x)próbuje przekonwertować przekazany parametr na liczbę 1 (równoważną Number(x)), a następnie sprawdza, czy wartość to NaN. Jeśli nie można przekonwertować parametru na liczbę, Number(x)zwróci wartość NaN2 . Dlatego, jeśli xwynikiem konwersji parametru na liczbę NaNjest wartość true; w przeciwnym razie zwraca false.

Tak więc w przypadku konkretnego x = null, nulljest konwertowana do liczby 0, (próby oceny Number(null)i zobaczyć, że zwraca 0,) i isNaN(0)zwraca wartość false. Łańcuch, który składa się tylko z cyfr, można przekształcić w liczbę, a isNaN zwraca również wartość false. Łańcuch (np. 'abcd'), Którego nie można przekonwertować na liczbę, spowoduje isNaN('abcd')zwrócenie wartości true, w szczególności dlatego, że Number('abcd')zwraca NaN.

Oprócz tych pozornych przypadków skrajnych są standardowe numeryczne przyczyny zwracania NaN, takie jak 0/0.

Jeśli chodzi o pozornie niespójne testy równości pokazane w pytaniu, zachowanie NaNjest określone w taki sposób, że każde porównanie x == NaNjest fałszywe, niezależnie od innego operandu, w tym NaNsamego siebie 1 .


8
BTW NaN !== NaN. Więc myślę, że nie jest do końca poprawne stwierdzenie, Number('abcd') == NaNponieważ Number('abcd')jest, NaNale nie jest równe NaN. Uwielbiam JavaScript.
nilfalse

Tak. Chciałem przekazać, że Number('abcd')jest NaN, ale zakłada się, że to prawda testuje równości, co nie jest prawdą. Zmienię to.
Glenn Moss

Zastanawiam się, dlaczego są isNaNi są Numberzaprojektowane tak, aby zachowywać się w ten sposób?
timidboy

1
Konwersja tylko nulldo 0(przynajmniej w tym kontekście) zachodzi w ramach isNaN()funkcji, co wymusza jej argument.
Glenn Moss

3
Number (null) == 0 but parseInt (null) == NaN love JS
JoshBerke

31

Właśnie sam napotkałem ten problem.

Dla mnie najlepszym sposobem użycia isNaN jest właśnie taki

isNaN(parseInt(myInt))

biorąc przykład phyzome z góry,

var x = [undefined, NaN,     'blah', 0/0,  null, 0,     '0',   1,     1/0, -1/0,  Number(5)]
x.map( function(n){ return isNaN(parseInt(n))})
        [true,      true,    true,   true, true, false, false, false, true, true, false]

(Wyrównałem wynik zgodnie z danymi wejściowymi, mam nadzieję, że ułatwi to odczytanie.)

Wydaje mi się to lepsze.


1
Nie będzie działać, jeśli myInt= „123d”. parseIntkonwertuje „123d” na 123, co następnie nie przechodzi isNaNtestu.
divesh premdeep

w istocie, jeśli chcesz uchwycić również ten scenariusz, proponuję połączyć moją odpowiedź i Glenna. który będzie wyglądał tak isNaN(parseInt(str,10)) || isNaN(Number()). btw - dla mnie, ponieważ muszę biec parseInt, aby użyć wartości liczbowej ciągu, uznanie „123d” za prawidłową liczbę jest w porządku. Jednak widzę potrzebę wykrycia również tego scenariusza.
facet mograbi

8

(Mój drugi komentarz dotyczy podejścia praktycznego. Oto strona teoretyczna).

Sprawdziłem standard ECMA 262 , który implementuje Javascript. Ich specyfikacja dla isNan:

Stosuje ToNumber do swojego argumentu, a następnie zwraca true, jeśli wynik to NaN, w przeciwnym razie zwraca false.

Rozdział 9.3 określa zachowanie ToNumber(które nie jest funkcją wywoływalną, ale raczej elementem systemu konwersji typów). Podsumowując tabelę, niektóre typy danych wejściowych mogą generować NaN. Są to typ undefined, typ number(ale tylko wartość NaN), dowolny obiekt, którego pierwotna reprezentacja to NaNi każdy string, którego nie można przeanalizować. Pozostawia to undefined, NaN, new Number(NaN), a większość struny.

Każdy taki wejściowy, który wytwarza NaNna wyjściu po przejściu do ToNumberbędzie produkować trueprzy zasilaniu isNaN. Ponieważ nullmożna z powodzeniem przekonwertować na liczbę, nie tworzy true.

I dlatego.


7

To jest rzeczywiście niepokojące. Oto tablica wartości, które przetestowałem:

var x = [undefined, NaN, 'blah', 0/0, null, 0, '0', 1, 1/0, -1/0, Number(5)]

Ocenia (w konsoli Firebug) na:

,NaN,blah,NaN,,0,0,1,Infinity,-Infinity,5

Kiedy dzwonię x.map(isNaN)(aby sprawdzić isNaN dla każdej wartości), otrzymuję:

true,true,true,true,false,false,false,false,false,false,false

Podsumowując, isNaNwygląda całkiem bezużytecznie! ( Edycja : z wyjątkiem tego, że okazuje się, że isNaN jest zdefiniowane tylko dla numeru, w którym to przypadku działa dobrze - tylko z wprowadzającą w błąd nazwą.)

Nawiasem mówiąc, oto typy tych wartości:

x.map(function(n){return typeof n})
-> undefined,number,string,number,object,number,string,number,number,number,number

Jak myślisz, co powinno oznaczać NaN? Jak myślisz, co jest tak mylącego w przypadku NaN lub w testowaniu go? Dlaczego jesteś zaniepokojony?
Bekim Bacaj

Cóż, to było 8 lat temu, ale wygląda na to, że przeszkadzało mi, że 1) ma niespójne wyniki dla wartości, które nie są typu Number, i 2) zawsze zwraca prawdę dla czegoś, co nie jest typu Number. Ponieważ łańcuch nie jest w rzeczywistości NaN. (Zobacz także moją drugą odpowiedź, która wyjaśnia, dlaczego tak się dzieje.)
traktuj dobrze swoje mody

5

Null nie jest NaN, podobnie jak łańcuch nie jest NaN. isNaN () po prostu sprawdź, czy naprawdę masz obiekt NaN.


Ale wtedy przynajmniej łańcuch jest rzutowany na obiekt NaN, ponieważ isNaN ("tekst") zwraca prawdę.
Hanno Fietz

1

Nie jestem do końca pewien, jeśli chodzi o JS, ale widziałem podobne rzeczy w innych językach i zwykle dzieje się tak dlatego, że funkcja sprawdza tylko, czy null jest dokładnie równe NaN (tj. Null === NaN byłoby fałszem). Innymi słowy, nie chodzi o to, że myśli, że null jest w rzeczywistości liczbą, ale raczej, że null nie jest NaN. Dzieje się tak prawdopodobnie dlatego, że oba są reprezentowane w JS w różny sposób, więc nie będą dokładnie równe, w taki sam sposób, jak 9! == '9'.



0

Uwaga:

"1" == 1 // true
"1" === 1 // false

Operator == dokonuje konwersji typu, podczas gdy === nie.

Witryna Douglasa Crockforda , Yahoo! Ewangelista JavaScript jest świetnym źródłem informacji o takich rzeczach.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.