Pourquoi isNaN («») (chaîne avec espaces) est-il égal à false?


160

En JavaScript, pourquoi isNaN(" ")évalue-t-il false, mais isNaN(" x")évalue- truet-il?

J'effectuer des opérations numériques sur un champ de saisie de texte, et je vérifie si le champ est null, ""ou NaN. Lorsque quelqu'un tape une poignée d'espaces dans le champ, ma validation échoue sur les trois et je ne comprends pas pourquoi elle dépasse le isNaNcontrôle.


1
Hm ... je ne sais pas trop où est passée l'autre moitié du sujet. Il est censé lire: "JavaScript: pourquoi isNaN (" ") est-il évalué à faux?"
IVR Avenger

Jes, c'est le comportement (vide ou espace retourne faux pour isNaN), mais je n'ai pas trouvé les spécifications exactes de cette fonction.
Lucero


Javascript sur ces questions semble être du vaudou! On ne sait jamais et l'explication est toujours assez complexe. "" == false // trueetisNaN(" ") // false
João Pimentel Ferreira

Réponses:


155

JavaScript interprète une chaîne vide comme un 0, ce qui échoue ensuite au test isNAN. Vous pouvez utiliser d'abord parseInt sur la chaîne, ce qui ne convertira pas la chaîne vide en 0. Le résultat devrait alors échouer isNAN.


53
Mais parseInt ("123abcd") renvoie 123, ce qui signifie que isNaN (parseInt ("123abcd")) retournera false alors qu'il devrait renvoyer true!
Pawan Nogariya

11
Alors que diriez-vous de (IsNaN (chaîne) || isNaN (parseInt (chaîne)))
matt

5
Il y a 2 étapes dans l'interprétation isNaN(arg). 1) Convertissez arg en nombre, 2) Vérifiez si ce nombre est la valeur numérique NaN. Cela m'a aidé à mieux comprendre.
xdhmoore

3
@Antonio_Haley Attends une minute, je ne comprends pas. Si "JavaScript interprète une chaîne vide comme un 0", pourquoi parseInt ("") renvoie-t-il NaN?
Jean-François Beauchamp

1
@ Jean-François Vous avez raison, la déclaration la plus correcte serait "isNaN interprète une chaîne vide comme 0", pas JavaScript lui-même.
Antonio Haley

82

Vous pouvez trouver cela surprenant ou peut-être pas, mais voici un code de test pour vous montrer la folie du moteur 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

donc cela signifie que

" " == 0 == false

et

"" == 0 == false

mais

"" != " "

S'amuser :)


5
+1 Excellent post. Pouvez-vous ajouter comment l'opérateur triple égal (=== et! ==) tient ici?
bendewey

2
Vous devriez essayer NaN === NaN ou NaN == NaN ;-) Je ne sais pas si tout cela signifie que le moteur javascript est farfelu ou que le javascript est mauvais pour les programmeurs farfelus.
KooiInc

10
@Kooilnc le fait que NaN! = NaN est, en fait, un bon choix pour une fois. L'idée est que NaN est presque toujours le résultat d'un calcul qui est allé différent de ce que le programmeur avait prévu, et supposer que les résultats de deux calculs qui ont «mal tourné» sont égaux est assez dangereux, je dirais.
skrebbel

2
@Kooilnc pour ne pas enlever même légèrement la folie du javascript, mais ces NaN obéissent simplement à la norme de virgule flottante IEEE 754. Vous pouvez tout lire à ce sujet comme d'habitude sur le grand W: en.wikipedia.org/wiki/NaN
Spike0xff

@NickBerardi F'ing LOL! Je suis tellement content d'avoir vu ce post. M'a aidé à comprendre pourquoi la fonction isNaN est si retardée. Je vais le retirer de mon code pas entièrement développé pour le moment et je ne l'utiliserai probablement plus jamais. Je valide pour null, ""et " "moi - même. Merci!
VoidKing

16

Pour mieux le comprendre, veuillez ouvrir le pdf de spécification Ecma-Script à la page 43 "ToNumber appliqué au type de chaîne"

si une chaîne a une syntaxe numérique, qui peut contenir n'importe quel nombre de caractères d'espacement, elle peut être convertie en type Number. La chaîne vide vaut 0. La chaîne «Infinity» doit également donner

isNaN('Infinity'); // false

13

Essayez d'utiliser:

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

Ou

alert(isNaN(parseFloat("    ")));

3
salut monsieur, isNaN (parseInt ("123a")): retournera 123 donc votre solution ne fonctionnera pas, si la chaîne contient aplha numeric
Sajjad Ali Khan

6

De la MDNraison du problème auquel vous êtes confronté

Lorsque l'argument de la fonction isNaN n'est pas de type Number, la valeur est d'abord forcée à un Number. La valeur résultante est ensuite testée pour déterminer s'il s'agit de NaN.

Vous voudrez peut-être vérifier la réponse complète suivante qui couvre également la comparaison NaN pour l'égalité.

Comment tester si une variable JavaScript est NaN


5

Je pense que c'est à cause du typage de Javascript: ' 'est converti à zéro, alors que ce 'x'n'est pas le cas:

alert(' ' * 1); // 0
alert('x' * 1); // NaN

4

Si vous souhaitez implémenter une fonction isNumber précise, voici une façon de le faire à partir de Javascript: The Good Parts par Douglas Crockford [page 105]

var isNumber = function isNumber(value) {
   return typeof value === 'number' && 
   isFinite(value);
}

4
@Xyan auquel cas cette fonction n'est pas très utile pour effectuer la tâche que l'OP demandait de faire, qui était d'inspecter une représentation sous forme de chaîne d'un nombre ...
ErikE

l'utilisation du soi-disant opérateur d'égalité stricte d'une valeur donnée par rapport à une chaîne littérale telle que "nombre" est stupide,
Bekim Bacaj

4

La réponse pas tout à fait correcte

La réponse très positive et acceptée d' Antonio Haley ici fait une fausse hypothèse que ce processus passe par la parseIntfonction de JavaScript :

Vous pouvez utiliser parseInt sur la chaîne ... Le résultat devrait alors échouer isNAN.

Nous pouvons facilement réfuter cette affirmation avec la chaîne "123abc":

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

Avec cela, nous pouvons voir que la parseIntfonction de JavaScript retourne "123abc"sous forme de nombre 123, mais sa isNaNfonction nous dit que ce "123abc" n'est pas un nombre.

La bonne réponse

ECMAScript-262 définit le fonctionnement du isNaNcontrôle dans la section 18.2.3 .

18.2.3 isNaN(nombre)

La isNaNfonction est l' %isNaN%objet intrinsèque. Lorsque la isNaNfonction est appelée avec un numéro d'argument, les étapes suivantes sont effectuées:

  1. Laisse numêtre? ToNumber(number).
  2. Si numc'est le cas NaN, revenez true.
  3. Sinon, revenez false.

La ToNumberfonction à laquelle elle fait référence est également définie dans la section 7.1.3 d'ECMAScript-262 . Ici, nous apprenons comment JavaScript gère les chaînes qui sont transmises à cette fonction.

Le premier exemple donné dans la question est une chaîne ne contenant que des espaces blancs. Cette section stipule que:

Un StringNumericLiteralqui est vide ou ne contient qu'un espace blanc est converti en +0.

L' " "exemple de chaîne est donc converti en +0, qui est un nombre.

La même section indique également:

Si la grammaire ne peut pas interpréter le Stringcomme une expansion de StringNumericLiteral, alors le résultat de ToNumberest NaN.

Sans citer tous les contrôles contenus dans cette section, l' " x"exemple donné dans la question relève de la condition ci-dessus car il ne peut pas être interprété comme un StringNumericLiteral. " x"est donc converti en NaN.


2

Je ne sais pas pourquoi , mais pour contourner le problème, vous pouvez toujours couper les espaces avant de vérifier. Vous voulez probablement le faire de toute façon.


4
une chaîne vide coupée échoue également au test isNaN.
Egemenk

2

La fonction isNaN("")effectue une coercition de type chaîne à nombre

ECMAScript 3-5 définit les valeurs de retour suivantes pour l'opérateur typeof:

  • indéfini
  • objet (null, objets, tableaux)
  • booléen
  • nombre
  • chaîne
  • fonction

Mieux vaut envelopper notre test dans un corps fonctionnel:

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

Cette fonction n'est pas destinée à tester le type de variable , mais teste la valeur forcée . Par exemple, les booléens et les chaînes sont forcés à des nombres, vous voudrez peut-être appeler cette fonction commeisNumberCoerced()

s'il n'est pas nécessaire de tester des types autres que string et number , alors l'extrait de code suivant peut être utilisé dans le cadre d'une condition:

if (!isNaN(s) && s.toString().trim()!='') // 's' can be boolean, number or string
    alert("s is a number")

1

Je vous suggère d'utiliser la fonction suivante si vous voulez vraiment vérifier correctement s'il s'agit d'un entier:

function isInteger(s)
{
   return Math.ceil(s) == Math.floor(s);
}

1

C'est isNaN(" ")faux fait partie du comportement déroutant de la isNaNfonction globale en raison de sa coercition des non-nombres vers un type numérique.

De MDN :

Depuis les toutes premières versions de la isNaNspécification de fonction, son comportement pour les arguments non numériques est déroutant. Lorsque l'argument de la isNaNfonction n'est pas de type Number, la valeur est d'abord convertie en Number. La valeur résultante est ensuite testée pour déterminer si c'est le cas NaN. Ainsi, pour les non-nombres qui, lorsqu'ils sont forcés au type numérique, aboutissent à une valeur numérique non NaN valide (notamment la chaîne vide et les primitives booléennes, qui lorsqu'elles sont forcées donnent des valeurs numériques zéro ou un), la valeur renvoyée "false" peut être inattendue; la chaîne vide, par exemple, n'est sûrement "pas un nombre".

Notez également qu'avec ECMAScript 6, il y a aussi maintenant la Number.isNaNméthode, qui selon MDN:

En comparaison avec la isNaN()fonction globale , Number.isNaN()ne souffre pas du problème de la conversion forcée du paramètre en nombre. Cela signifie qu'il est maintenant sûr de transmettre des valeurs qui seraient normalement converties en NaN, mais qui ne sont pas réellement la même valeur que NaN. Cela signifie également que seules les valeurs du type number, qui sont également NaN, retournent true.

Malheureusement :

Même la Number.isNaNméthode ECMAScript 6 a ses propres problèmes, comme indiqué dans le billet de blog - Résoudre le problème laid de JavaScript et ES6 NaN .


1

La isNaNfonction attend un nombre comme argument, donc les arguments de tout autre type (dans votre cas, une chaîne) seront convertis en nombre avant que la logique de la fonction réelle ne soit exécutée. (Sachez qu'il NaNs'agit également d'une valeur de type Number!)

Btw. ceci est courant pour toutes les fonctions intégrées - si elles attendent un argument d'un certain type, l'argument réel sera converti en utilisant les fonctions de conversion standard. Il existe des conversions standard entre tous les types de base (booléen, chaîne, nombre, objet, date, null, indéfini.)

La conversion standard pour Stringto Numberpeut être invoquée explicitement avec Number(). Nous pouvons donc voir que:

  • Number(" ") évalue à 0
  • Number(" x") évalue à NaN

Compte tenu de cela, le résultat de la isNaNfonction est tout à fait logique!

La vraie question est de savoir pourquoi la conversion standard de chaîne en nombre fonctionne comme elle le fait. La conversion chaîne en nombre est vraiment destinée à convertir des chaînes numériques comme "123" ou "17.5e4" en nombres équivalents. La conversion saute d'abord l'espace blanc initial (donc "123" est valide), puis essaie d'analyser le reste comme un nombre. S'il n'est pas analysable en tant que nombre ("x" ne l'est pas), le résultat est NaN. Mais il y a la règle spéciale explicite selon laquelle une chaîne qui est vide ou uniquement des espaces est convertie en 0. Cela explique donc la conversion.

Référence: http://www.ecma-international.org/ecma-262/5.1/#sec-9.3.1


1

J'ai écrit cette petite fonction rapide pour aider à résoudre ce problème.

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

Il vérifie simplement tous les caractères qui ne sont pas numériques (0-9), qui ne sont pas «-» ou «.», Et qui ne sont pas indéfinis, nuls ou vides et renvoie true s'il n'y a pas de correspondance. :)


Un merci tardif pour cela; cela a très bien résolu mon problème.
Henry le

1

Comme d'autres l'ont expliqué, la isNaNfonction contraindra la chaîne vide en un nombre avant de la valider, changeant ainsi une chaîne vide en 0 (qui est un nombre valide). Cependant, j'ai trouvé que la parseIntfonction reviendrait NaNen essayant d'analyser une chaîne vide ou une chaîne avec uniquement des espaces. En tant que telle, la combinaison suivante semble bien fonctionner:

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

Cette vérification fonctionnera pour les nombres positifs, les nombres négatifs et les nombres avec une virgule décimale, donc je pense qu'elle couvre tous les cas numériques courants.


1

NaN ! == "pas un nombre"

NaN est une valeur de Type de nombre

c'est une définition de isNaN () dans ECMAScript

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

Essayez de convertir n'importe quelle valeur en nombre.

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

Si vous souhaitez déterminer si la valeur est NaN, vous devez d'abord essayer de la convertir en valeur numérique.


0

Cette fonction a semblé fonctionner dans mes tests

function isNumber(s) {
    if (s === "" || s === null) {
        return false;
    } else {
        var number = parseInt(s);
        if (number == 'NaN') {
            return false;
        } else {
            return true;
        }
    }
}

2
Votre fonction entière peut être écrite:return !(s === "" || s === null || parseInt(s) == 'NaN');
ErikE

0

Qu'en est-il de

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

0

Le JavaScript intégré isNaN fonction, est - comme cela devrait être attendu par défaut - un « type dynamique de l' opérateur ». Par conséquent, toutes les valeurs qui (pendant le processus DTC) peuvent donner un simple true | false tel que "", " ", " 000", ne peut pas être NaN.

Cela signifie que l' argument fourni subira d'abord une conversion comme dans:

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

Explication:

Dans la ligne supérieure du corps de la fonction, nous essayons (d'abord) de convertir avec succès l'argument en un objet numérique. Et (deuxièmement), en utilisant l'opérateur point, nous supprimons immédiatement, pour notre propre convenance, la valeur primitive de l'objet créé.

Dans la deuxième ligne, on prend la valeur obtenue à l'étape précédente, et l'avantage du fait que NaN n'est égal à rien dans l'univers, pas même à lui-même, par exemple: NaN == NaN >> falsepour enfin le comparer (pour l'inégalité) avec lui-même .

De cette façon, la fonction return ne donnera vrai que lorsque, et seulement si, l'argument-retour fourni est une tentative infructueuse de conversion en un objet numérique, c'est-à-dire un nombre non-un-nombre; par exemple, NaN.


isNaNstatic ()

Cependant, pour un opérateur de type statique - si nécessaire et si nécessaire - nous pouvons écrire une fonction beaucoup plus simple telle que:

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

Et évitez complètement le DTC afin que si l'argument n'est pas explicitement un nombre NaN, il renverra faux. Par conséquent, testez les éléments suivants:

isNaNStatic(" x"); // will return false parce que c'est toujours une chaîne.

Cependant: isNaNStatic(1/"x"); // will of course return true.comme par exemple isNaNStatic(NaN); >> true.

Mais contrairement à isNaN, le isNaNStatic("NaN"); >> falsecar il (l'argument) est une chaîne ordinaire.

ps: La version statique d'isNaN peut être très utile dans les scénarios de codage modernes. Et cela peut très bien être l'une des principales raisons pour lesquelles j'ai pris mon temps pour publier ceci.

Cordialement.


0

isNAN(<argument>)est une fonction pour dire si l'argument donné est un nombre illégal. isNaNtype convertit les arguments en type Number. Si vous voulez vérifier si l'argument est numérique ou non? Veuillez utiliser la $.isNumeric()fonction dans jQuery.

Autrement dit, isNaN (foo) est équivalent à isNaN (Number (foo)) Il accepte toutes les chaînes ayant tous les chiffres comme des nombres pour des raisons évidentes. Par ex.

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

0

Je l'utilise

    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


0

Lors de la vérification si une certaine valeur de chaîne avec un espace ou " "est isNaNpeut - être essayez d'effectuer une validation de chaîne, exemple:

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.