Dlaczego isNaN („”) (łańcuch ze spacjami) jest równe fałszowi?

160

Dlaczego w JavaScript isNaN(" ")ocenia się na false, ale isNaN(" x")ocenia na true?

Mam wykonywania operacji numerycznych na polu tekstowym, a ja sprawdzenie, czy pole jest null, ""albo NaN. Kiedy ktoś wpisze w to pole kilka spacji, moja walidacja kończy się niepowodzeniem we wszystkich trzech i jestem zdezorientowany, dlaczego przeszedł isNaNkontrolę.

IVR Avenger
źródło
1
Hm ... nie do końca jestem pewien, dokąd poszła druga połowa tematu. Powinien brzmieć: „JavaScript: Dlaczego isNaN („ ”) przyjmuje wartość fałsz?”
IVR Avenger
Tak, to jest zachowanie (puste lub spacja zwraca wartość false dla isNaN), ale nie znalazłem dokładnych specyfikacji tej funkcji.
Lucero
Javascript w tych sprawach wygląda jak voodoo! Nigdy nie wiadomo, a wyjaśnienie jest zawsze dość złożone. "" == false // trueorazisNaN(" ") // false
João Pimentel Ferreira

Odpowiedzi:

155

JavaScript interpretuje pusty ciąg jako 0, co następnie kończy się niepowodzeniem w teście isNAN. Możesz najpierw użyć parseInt na łańcuchu, co nie spowoduje konwersji pustego ciągu na 0. Wynik powinien wtedy zakończyć się niepowodzeniem toNAN.

Antonio Haley
źródło
53
Ale parseInt ("123abcd") zwraca 123, co oznacza, że ​​isNaN (parseInt ("123abcd")) zwróci fałsz, podczas gdy powinno zwrócić prawdę!
Pawan Nogariya
11
A co powiesz na (IsNaN (string) || isNaN (parseInt (string)))
Matt
5
Tłumaczenie ustne składa się z dwóch kroków isNaN(arg). 1) Zamień argument na liczbę, 2) Sprawdź, czy ta liczba jest wartością liczbową NaN. To pomogło mi lepiej to zrozumieć.
xdhmoore,
3
@Antonio_Haley Poczekaj chwilę, nie rozumiem. Jeśli „JavaScript interpretuje pusty ciąg jako 0”, dlaczego parseInt („”) zwraca NaN?
Jean-François Beauchamp
1
@ Jean-François Masz rację, bardziej poprawne byłoby stwierdzenie „isNaN interpretuje pusty ciąg jako 0”, a nie sam JavaScript.
Antonio Haley
82

Może się to wydawać zaskakujące, ale może nie, ale oto kod testowy, który pokaże ci zwariowanie silnika JavaScript.

document.write(isNaN("")) // false
document.write(isNaN(" "))  // false
document.write(isNaN(0))  // false
document.write(isNaN(null)) // false
document.write(isNaN(false))  // false
document.write("" == false)  // true
document.write("" == 0)  // true
document.write(" " == 0)  // true
document.write(" " == false)  // true
document.write(0 == false) // true
document.write(" " == "") // false

więc to znaczy, że

" " == 0 == false

i

"" == 0 == false

ale

"" != " "

Baw się dobrze :)

Nick Berardi
źródło
5
+1 Świetny post. Czy możesz dodać, jak operator potrójnej równości (=== i! ==) pasuje tutaj?
bendewey
2
Powinieneś spróbować NaN === NaN lub NaN == NaN ;-) Nie wiem, czy to wszystko oznacza, że ​​silnik javascript jest zwariowany, czy też javascript jest zły dla zwariowanych programistów.
KooiInc
10
@Kooilnc fakt, że NaN! = NaN jest właściwie dobrym wyborem, chociaż raz. Chodzi o to, że NaN prawie zawsze jest wynikiem obliczenia, które poszło inaczej niż zamierzał programista, i powiedziałbym, że założenie, że wyniki dwóch obliczeń, które poszły „źle”, są równe, jest dość niebezpieczne.
skrebbel
2
@Kooilnc, aby nie odbierać nawet trochę głupoty javascript, ale te NaN są po prostu zgodne ze standardem zmiennoprzecinkowym IEEE 754. Możesz przeczytać WSZYSTKIE na ten temat jak zwykle na dużym W: en.wikipedia.org/wiki/NaN
Spike0xff
@NickBerardi F'ing LOL! Bardzo się cieszę, że zobaczyłem ten post. Pomogło mi zrozumieć, dlaczego funkcja isNaN jest tak opóźniona. Będę go teraz usuwać z mojego nie w pełni rozwiniętego kodu i prawdopodobnie nigdy więcej go nie użyję. Będę walidacji dla null, ""i " "ja. Dzięki!
VoidKing
16

Aby lepiej to zrozumieć, otwórz specyfikację Ecma-Script pdf na stronie 43 „ToNumber zastosowany do typu ciągu”

jeśli ciąg ma składnię numeryczną, która może zawierać dowolną liczbę białych znaków, można go przekonwertować na typ Number. Pusty łańcuch ma wartość 0. Również powinien dać ciąg „Nieskończoność”

isNaN('Infinity'); // false
Rafael
źródło
13

Spróbuj użyć:

alert(isNaN(parseInt("   ")));

Lub

alert(isNaN(parseFloat("    ")));
Bendewey
źródło
3
cześć proszę pana, isNaN (parseInt ("123a")): zwróci 123, więc nasze rozwiązanie nie będzie działać, jeśli ciąg zawiera numeryczną aplha
Sajjad Ali Khan
6

Z MDNpowodu problemu, przed którym stoisz

Gdy argument funkcji isNaN nie jest typu Number, wartość jest najpierw przekształcana w Number. Wynikowa wartość jest następnie testowana w celu określenia, czy jest to NaN.

Możesz chcieć sprawdzić następującą kompleksową odpowiedź, która obejmuje również porównanie NaN dla równości.

Jak sprawdzić, czy zmienną JavaScript jest NaN

dopeddude
źródło
5

Myślę, że to z powodu pisania w Javascript: ' 'jest konwertowane na zero, a 'x'nie:

alert(' ' * 1); // 0
alert('x' * 1); // NaN
Greg
źródło
4

Jeśli chcesz zaimplementować dokładną funkcję isNumber, oto jeden ze sposobów na zrobienie tego z Javascript: The Good Parts autorstwa Douglasa Crockforda [strona 105]

var isNumber = function isNumber(value) {
   return typeof value === 'number' && 
   isFinite(value);
}
Brian Grinstead
źródło
4
@Xyan w takim przypadku ta funkcja nie jest zbyt pomocna w wykonywaniu zadania, o które prosił OP, polegającego na sprawdzeniu ciągu reprezentującego liczbę ...
ErikE
używanie tak zwanego operatora ścisłej równości o dowolnej wartości w stosunku do literału ciągu, takiego jak „liczba”, jest głupie,
Bekim Bacaj
4

Odpowiedź nie do końca poprawna

Bardzo pozytywna i akceptowana tutaj odpowiedź Antonio Haleya zakłada błędne założenie, że ten proces przechodzi przez parseIntfunkcję JavaScript :

Możesz użyć parseInt na łańcuchu ... Wynik powinien wtedy zakończyć się niepowodzeniem toNAN.

Możemy łatwo obalić to stwierdzenie za pomocą łańcucha "123abc":

parseInt("123abc")    // 123     (a number...
isNaN("123abc")       // true     ...which is not a number)

Dzięki temu możemy zobaczyć, że parseIntfunkcja JavaScript zwraca "123abc"jako liczbę 123, ale jej isNaNfunkcja mówi nam, że "123abc" tak nie jest jest liczbą.

Poprawna odpowiedź

ECMAScript-262 definiuje sposób działania isNaNsprawdzenia w sekcji 18.2.3 .

18.2.3 isNaN(liczba)

isNaNFunkcja jest %isNaN%nierozerwalnie obiektu. Gdy isNaNfunkcja jest wywoływana z jednym numerem argumentu, podejmowane są następujące kroki:

  1. Niech numbędzie? ToNumber(number).
  2. Jeśli numtak NaN, wróć true.
  3. W przeciwnym razie wróć false.

Te ToNumberodniesienia funkcją jest również zdefiniowane w ECMAScript-262 w sekcji 7.1.3 . Tutaj dowiadujemy się, jak JavaScript obsługuje ciągi znaków przekazywane do tej funkcji.

Pierwszy przykład podany w pytaniu to łańcuch zawierający tylko znaki odstępu. Ta sekcja stwierdza, że:

A, StringNumericLiteralktóry jest pusty lub zawiera tylko spacje, jest konwertowany na +0.

" "Przykład łańcuch jest zatem przekształca się +0, co jest liczbą.

Ta sama sekcja stwierdza również:

Jeśli gramatyka nie może zinterpretować wyrażenia Stringjako rozwinięcie StringNumericLiteral, to wynik ToNumberjest NaN.

Bez cytowania wszystkich kontroli zawartych w tej sekcji, " x"przykład podany w pytaniu mieści się w powyższym warunku, ponieważ nie może być interpretowany jako a StringNumericLiteral. " x"jest zatem konwertowany na NaN.

James Donnelly
źródło
2

Nie jestem pewien, dlaczego , ale aby obejść problem, zawsze można było skrócić spacje przed sprawdzeniem. Prawdopodobnie i tak chcesz to zrobić.

Joel Coehoorn
źródło
4
przycięty pusty ciąg również nie przechodzi testu isNaN.
Egemenk
2

Funkcja isNaN("")wykonuje wymuszenie typu ciąg na liczbę

ECMAScript 3-5 definiuje następujące wartości zwracane dla operatora typeof:

  • nieokreślony
  • obiekt (null, obiekty, tablice)
  • boolean
  • numer
  • strunowy
  • funkcjonować

Lepiej zawrzeć nasz test w treści funkcji:

function isNumber (s) {
    return typeof s == 'number'? true
           : typeof s == 'string'? (s.trim() === ''? false : !isNaN(s))
           : (typeof s).match(/object|function/)? false
           : !isNaN(s)
}

Ta funkcja nie jest przeznaczona do testowania typu zmiennej , zamiast tego testuje wymuszoną wartość . Na przykład wartości logiczne i ciągi znaków są przekształcane na liczby, więc być może warto nazwać tę funkcję jakoisNumberCoerced()

jeśli nie ma potrzeby testowania typów innych niż ciąg i liczba , następujący fragment może zostać użyty jako część warunku:

if (!isNaN(s) && s.toString().trim()!='') // 's' can be boolean, number or string
    alert("s is a number")
Steven Pribilinskiy
źródło
1

Proponuję użyć następującej funkcji, jeśli naprawdę chcesz poprawnie sprawdzić, czy jest to liczba całkowita:

function isInteger(s)
{
   return Math.ceil(s) == Math.floor(s);
}
Bat_Programmer
źródło
1

To isNaN(" ")jest fałsz jest częścią zagmatwanego zachowania isNaNfunkcji globalnej z powodu jej przymusu przekształcania nieliczbowych w typ liczbowy.

Z MDN :

Od najwcześniejszych wersji isNaNspecyfikacji funkcji jej zachowanie dla argumentów nieliczbowych było mylące. Gdy argument isNaNfunkcji nie jest typu Number, wartość jest najpierw przekształcana w Number. Wynikowa wartość jest następnie testowana w celu określenia, czy tak jest NaN. Tak więc dla nie-liczb, które po przekształceniu do typu liczbowego dają prawidłową wartość liczbową inną niż NaN (w szczególności pusty łańcuch i wartości logiczne, które po wymuszeniu dają wartości liczbowe zero lub jeden), zwrócona wartość „fałsz” może być nieoczekiwana; na przykład pusty ciąg z pewnością nie jest liczbą.

Zauważ również, że w ECMAScript 6 istnieje teraz również Number.isNaNmetoda, która według MDN:

W porównaniu z isNaN()funkcją globalną Number.isNaN()nie ma problemu z wymuszoną konwersją parametru na liczbę. Oznacza to, że można teraz bezpiecznie przekazywać wartości, które normalnie byłyby konwertowane NaN, ale w rzeczywistości nie są takie same jak NaN. Oznacza to również, że tylko wartości liczby typu, które są także NaN, powrót true.

Niestety :

Nawet Number.isNaNmetoda ECMAScript 6 ma swoje własne problemy, które opisano w poście na blogu - Naprawianie brzydkiego problemu JavaScript i ES6 NaN .

lucono
źródło
1

Plik isNaNFunkcja oczekuje numeru jako argument, więc argumenty innego rodzaju (w przypadku łańcuch) zostaną przekształcone w Number przed rzeczywista logika funkcja jest wykonywana. (Należy pamiętać, że NaNjest to również wartość typu Number!)

Przy okazji. to jest wspólne dla wszystkich funkcji wbudowanych - jeśli oczekują argumentu określonego typu, rzeczywisty argument zostanie przekonwertowany przy użyciu standardowych funkcji konwersji. Istnieją standardowe konwersje między wszystkimi podstawowymi typami (bool, string, number, object, date, null, undefined).

Standardową konwersję for Stringdo Numbermożna wywołać jawnie za pomocąNumber() . Widzimy więc, że:

  • Number(" ") ocenia do 0
  • Number(" x") ocenia do NaN

Biorąc to pod uwagę, wynik isNaNfunkcji jest całkowicie logiczny!

Prawdziwe pytanie brzmi, dlaczego standardowa konwersja ciągów na liczbę działa tak, jak to robi. Konwersja ciągów na liczby ma w rzeczywistości na celu konwersję ciągów liczbowych, takich jak „123” lub „17,5e4”, na równoważne liczby. Konwersja najpierw pomija początkowe białe znaki (więc „123” jest poprawne), a następnie próbuje przeanalizować resztę jako liczbę. Jeśli nie można jej analizować jako liczby („x” nie jest), to wynikiem jest NaN. Ale istnieje specjalna, wyraźna reguła, że ​​ciąg, który jest pusty lub zawiera tylko białe znaki, jest konwertowany na 0. To wyjaśnia konwersję.

Źródła: http://www.ecma-international.org/ecma-262/5.1/#sec-9.3.1

JacquesB
źródło
1

Napisałem tę krótką funkcję, aby pomóc rozwiązać ten problem.

function isNumber(val) {
     return (val != undefined && val != null && val.toString().length > 0 && val.toString().match(/[^0-9\.\-]/g) == null);
};

Po prostu sprawdza wszystkie znaki, które nie są liczbami (0-9), które nie są „-” ani „.”, I które nie są niezdefiniowane, puste lub puste i zwraca wartość „prawda”, jeśli nie ma dopasowań. :)

XtraSimplicity
źródło
Spóźnione podziękowania za to; to bardzo ładnie rozwiązało mój problem.
Henry
1

Jak wyjaśnili inni, isNaNfunkcja przekształci pusty ciąg w liczbę przed jej walidacją, zmieniając w ten sposób pusty ciąg na 0 (co jest prawidłową liczbą). Jednak odkryłem, że parseIntfunkcja zwróci NaNpodczas próby przeanalizowania pustego ciągu lub ciągu zawierającego tylko spacje. Jako taka wydaje się, że następująca kombinacja działa dobrze:

if ( isNaN(string) || isNaN(parseInt(string)) ) console.log('Not a number!');

To sprawdzenie będzie działać dla liczb dodatnich, ujemnych i liczb z przecinkiem, więc uważam, że obejmuje wszystkie typowe przypadki liczbowe.

Nadav
źródło
1

NaN ! == "nie jest liczbą"

NaN jest wartością typu liczbowego

to jest definicja isNaN () w ECMAScript

1. Let num be ToNumber(number).
2. ReturnIfAbrupt(num).
3. If num is NaN, return true.
4. Otherwise, return false.

Spróbuj przekonwertować dowolną wartość na liczbę.

Number(" ") // 0
Number("x") // NaN
Number(null) // 0

Jeśli chcesz ustalić, czy wartość jest równa NaN, powinieneś najpierw spróbować przekonwertować ją na wartość liczbową.

bitfishxyz
źródło
0

Ta funkcja wydawała się działać w moich testach

function isNumber(s) {
    if (s === "" || s === null) {
        return false;
    } else {
        var number = parseInt(s);
        if (number == 'NaN') {
            return false;
        } else {
            return true;
        }
    }
}
bruno negrao
źródło
2
Całą swoją funkcję można zapisać:return !(s === "" || s === null || parseInt(s) == 'NaN');
ErikE
0

Co powiesz na

function isNumberRegex(value) {        
    var pattern = /^[-+]?\d*\.?\d*$/i;
    var match = value.match(pattern);
    return value.length > 0 && match != null;
}
Alexander Schmidt
źródło
0

JavaScript wbudowanej isNaN funkcji, jest - jak należy oczekiwać domyślnie - „Dynamiczny Type Operator”. Dlatego wszystkie wartości, które (podczas procesu DTC) mogą dawać prostą prawdę | fałszywe, takie jak"", " ", " 000" , nie może być NaN.

Oznacza to, że podany argument zostanie najpierw poddany konwersji, jak w:

function isNaNDemo(arg){
   var x = new Number(arg).valueOf();
   return x != x;
}

Wyjaśnienie:

W górnym wierszu treści funkcji (najpierw) próbujemy pomyślnie przekształcić argument na obiekt liczbowy. I (drugi), za pomocą operatora kropki jesteśmy - dla własnej wygody - natychmiast zdzierając, prymitywny wartość tworzonego obiektu.

W drugim wierszu bierzemy wartość uzyskaną w poprzednim kroku oraz zaletę faktu, że NaN nie jest równe żadnemu we wszechświecie, nawet sobie, np:NaN == NaN >> false aby ostatecznie porównać ją (pod kątem nierówności) ze sobą .

W ten sposób funkcja return zwróci wartość true tylko wtedy i tylko wtedy, gdy podany argument-return jest nieudaną próbą konwersji na obiekt liczbowy, tj. Liczbę niebędącą liczbą; np. NaN.


isNaNstatic ()

Jednak dla operatora typu statycznego - w razie potrzeby i kiedy potrzeba - możemy napisać znacznie prostszą funkcję, taką jak:

function isNaNstatic(x){   
   return x != x;
}

I całkowicie unikaj DTC, aby jeśli argument nie był jawnie liczbą NaN, zwrócił fałsz. Dlatego testowanie pod kątem:

isNaNStatic(" x"); // will return false ponieważ nadal jest to ciąg.

Jednak: isNaNStatic(1/"x"); // will of course return true.tak jak na przykładisNaNStatic(NaN); >> true .

Ale w przeciwieństwie do tego isNaN, isNaNStatic("NaN"); >> falseponieważ to (argument) jest zwykłym ciągiem.

ps: Statyczna wersja isNaN może być bardzo przydatna w nowoczesnych scenariuszach kodowania. I może to być jeden z głównych powodów, dla których nie spieszyłem się z opublikowaniem tego.

Pozdrowienia.

Bekim Bacaj
źródło
0

isNAN(<argument>)jest funkcją, która mówi, czy dany argument jest niedozwoloną liczbą. isNaNtypekastruje argumenty do typu Number. Jeśli chcesz sprawdzić, czy argument jest numeryczny, czy nie? Proszę użyć $.isNumeric()funkcji w jQuery.

Oznacza to, że isNaN (foo) jest równoważne isNaN (Number (foo)). Z oczywistych powodów akceptuje wszystkie łańcuchy zawierające wszystkie cyfry jako liczby. Na przykład.

isNaN(123) //false
isNaN(-1.23) //false
isNaN(5-2) //false
isNaN(0) //false
isNaN('123') //false
isNaN('Hello') //true
isNaN('2005/12/12') //true
isNaN('') //false
isNaN(true) //false
isNaN(undefined) //true
isNaN('NaN') //true
isNaN(NaN) //true
isNaN(0 / 0) //true
Om Sao
źródło
0

używam tego

    function isNotANumeric(val) {
    	if(val.trim && val.trim() == "") {
         return true;
      } else {
      	 return isNaN(parseFloat(val * 1));
      }
    }
    
    alert(isNotANumeric("100"));  // false
    alert(isNotANumeric("1a"));   // true
    alert(isNotANumeric(""));     // true
    alert(isNotANumeric("   "));  // true

kiranvj
źródło
0

Przy sprawdzaniu, czy pewnej wartości ciąg z odstępami lub " "jest isNaNmoże próbować wykonać walidację ciąg, przykład:

// value = "123 " if (value.match(/\s/) || isNaN(value)) { // do something }

Channox
źródło