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.
Hanno Fietz
źródło
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 .

Glenn Moss
źródło
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.

facet mograbi
źródło
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.

dobrze traktuj swoje mody
źródło
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
dobrze traktuj swoje mody
źródło
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.

gadżet
źródło
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'.

Jason Tennier
źródło
1

W ES5 zdefiniowano jako isNaN (number)zwraca prawdę, jeśli argument wymusza na NaN, aw przeciwnym razie zwraca fałsz.

  • Jeśli ToNumber (liczba) ma wartość NaN, zwraca wartość true.
  • W przeciwnym razie zwróć false.

I zobacz tabelę konwersji operacji abstrakcyjnej ToNumber . Więc wewnętrznie ocena silnika js ToNumber(Null)jest +0, a ostatecznie isNaN(null)jestfalse

rab
źródło
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.

cllpse
źródło