Pourquoi `null> = 0 && null <= 0` mais pas` null == 0`?

142

J'ai dû écrire une routine qui incrémente la valeur d'une variable de 1 si son type est numberet affecte 0 à la variable sinon, où la variable est initialement nullou undefined.

La première implémentation était v >= 0 ? v += 1 : v = 0parce que je pensais que tout ce qui n'est pas un nombre rendrait une expression arithmétique fausse, mais c'était faux car il null >= 0est évalué à vrai. Ensuite, j'ai appris qu'il nullse comporte comme 0 et les expressions suivantes sont toutes évaluées à vrai.

  • null >= 0 && null <= 0
  • !(null < 0 || null > 0)
  • null + 1 === 1
  • 1 / null === Infinity
  • Math.pow(42, null) === 1

Bien sûr, nulln'est pas 0. null == 0est évalué à faux. Cela rend l'expression apparemment tautologique (v >= 0 && v <= 0) === (v == 0)fausse.

Pourquoi est nullcomme 0, bien que ce ne soit pas réellement 0?

C. Lee
la source
3
Il parle de Javascript. Votre exemple est en PHP. En PHP, l'opérateur == compare les valeurs d'une manière spéciale. Vous pouvez faire des comparaisons vraiment folles comme "10" == "1e1" (ce qui est vrai). Si vous avez utilisé l'opérateur ===, vous obtiendrez un résultat complètement différent car il vérifie si le type correspond ainsi que la valeur. Consultez ce lien: php.net/manual/en/language.operators.comparison.php
Pijusn
L'opérateur PHP '==' fonctionne vraiment de manière "spéciale".
Two-Bit Alchemist
Si votre exigence était de commencer à compter à 1 au lieu de 0, il existe un moyen très laconique d'incrémenter les compteurs qui sont initialement soit nullsoit undefined:c = -~c // Results in 1 for null/undefined; increments if already a number
Ates Goral
1
undefinedest une valeur de variable, pour les variables qui n'ont pas été initialisées. null, d'autre part, est une valeur d'objet vide et ne doit pas être mélangée avec des nombres. nullne doit pas être combiné avec des nombres, donc null ne devrait pas avoir à se comporter comme des nombres.
Matthieu
1
@AtesGoral - concis, mais non évident. Il convient de rappeler aux gens que chaque fois que vous faites quelque chose de non évident, veuillez ajouter un commentaire expliquant ce que fait le code. Dans la plupart des situations, je la considérerais comme une "optimisation prématurée", étant donné qu'elle échange la clarté contre un gain de performance minuscule.
ToolmakerSteve

Réponses:

207

Votre vraie question semble être:

Pourquoi:

null >= 0; // true

Mais:

null == 0; // false

Ce qui se passe vraiment, c'est que l' Opérateur Supérieur ou égal ( >=), effectue la coercition de type ( ToPrimitive), avec un type d' indication de Number, en fait tous les opérateurs relationnels ont ce comportement.

nullest traité d'une manière spéciale par l' opérateur égal ( ==). Dans un bref, il ne contraint qu'à undefined:

null == null; // true
null == undefined; // true

Valeur telle que false, '', '0'et []sont soumis à la contrainte de type numérique, tous les contraignent à zéro.

Vous pouvez voir les détails internes de ce processus dans l' algorithme de comparaison d'égalité abstraite et l'algorithme de comparaison relationnelle abstraite .

En résumé:

  • Comparaison relationnelle: si les deux valeurs ne sont pas de type String, ToNumberest appelée sur les deux. C'est la même chose que d'ajouter un +devant, qui pour null contraint à 0.

  • Comparaison d'égalité: n'appelle que ToNumberles chaînes, les nombres et les booléens.

CMS
la source
1
Salut CMS, selon votre explication, la primitive null est 0, donc 0> = 0 renvoie true et == renvoie false.Mais selon l'algorithme ecma Si Type (x) est Object et Type (y) est soit String ou Number, retourne le résultat de la comparaison ToPrimitive (x) == y.alors il devrait renvoyer true.please expliquez-moi
bharath muppa
pour moi, la réponse ne fournit pas de réponse - null is treated in a special way by the Equals Operator (==). In a brief, it only coerces to undefined:- et quoi? Pouvez-vous expliquer pourquoi null >= 0? :)
Andrey Deineko
@bharathmuppa @ andrey-deineko: Le reste de la réponse de CMS est ici: L'algorithme de comparaison relationnelle abstraite qui explique au point 3. que si les deux valeurs ne sont pas de type String, ToNumber est appelé sur les deux. C'est la même chose que d'ajouter un +devant, qui pour null contraint à 0. L'égalité appelle uniquement ToNumber sur les chaînes, les nombres et les booléens.
Michael Liquori
7
Bonne description, mais je n'aime pas ça. Dans n'importe quelle langue (x == 0 || x> 0) doit être équivalent à (x> = 0). javascript est un langage stupide.
John Henckel
1
C'est simplement un bogue dans la spécification (parce que mathématiquement, c'est faux) et il n'y a rien à faire à ce sujet puisque des millions de sites Web reposent sur des comparaisons nulles ^^ '
mahieddine
14

Je voudrais étendre la question pour améliorer encore la visibilité du problème:

null >= 0; //true
null <= 0; //true
null == 0; //false
null > 0;  //false
null < 0;  //false

Cela n'a tout simplement aucun sens. Comme les langues humaines, ces choses doivent être apprises par cœur.

estani
la source
1
Comme décrit ci-dessus, il peut être expliqué à une exception près comme comment == traite null, sinon, dans tous les cas, null est converti en 0 en utilisant Number (nulll)
Sourabh Ranka
5

JavaScript propose des comparaisons strictes et de conversion de type

null >= 0;est vrai mais (null==0)||(null>0)est faux

null <= 0;est vrai mais (null==0)||(null<0)est faux

"" >= 0 est aussi vrai

Pour les comparaisons abstraites relationnelles (<=,> =), les opérandes sont d'abord convertis en primitives, puis du même type, avant comparaison.

typeof null returns "object"

Lorsque le type est un objet, javascript tente de stringifier l'objet (c'est-à-dire null), les étapes suivantes sont suivies ( ECMAScript 2015 ):

  1. Si ce PreferredTypen'est pas passé, laissez hintêtre "par défaut".
  2. Sinon, si PreferredTypeest hintString, hintsoit "string".
  3. Else PreferredTypeest hintNombre, nous allons hintêtre « nombre ».
  4. Laissez exoticToPrimêtre GetMethod(input, @@toPrimitive).
  5. ReturnIfAbrupt(exoticToPrim).
  6. Si exoticToPrimn'est pas indéfini, alors
    a) Soit le résultat Call(exoticToPrim, input, «hint»).
    b) ReturnIfAbrupt(result).
    c) Si Type(result)n'est pas Object, renvoie le résultat.
    d) Lancez une exception TypeError.
  7. Si hintest "par défaut", hintsoit "nombre".
  8. Retour OrdinaryToPrimitive(input,hint).

Les valeurs autorisées pour l'indice sont "par défaut", "nombre" et "chaîne". Les objets Date sont uniques parmi les objets ECMAScript intégrés en ce sens qu'ils traitent "default" comme étant équivalent à "string". Tous les autres objets ECMAScript intégrés considèrent «default» comme étant équivalent à «number» . ( ECMAScript 20.3.4.45 )

Donc je pense qu'il se nullconvertit à 0.

Panos Kal.
la source
1

J'ai eu le même problème !!. Actuellement, ma seule solution est de me séparer.

var a = null;
var b = undefined;

if (a===0||a>0){ } //return false  !work!
if (b===0||b>0){ } //return false  !work!

//but 
if (a>=0){ } //return true !
Jon
la source
Il pourrait être plus clair à faire à la place: if (a!=null && a>=0). Ceci clarifie la raison de ne pas simplement faire >=par lui-même: "a pourrait être nul (ou non défini, qui est aussi '== null')".
ToolmakerSteve
0
console.log( null > 0 );  // (1) false
console.log( null == 0 ); // (2) false
console.log( null >= 0 ); // (3) true

Mathématiquement, c'est étrange. Le dernier résultat indique que "null est supérieur ou égal à zéro", donc dans l'une des comparaisons ci-dessus, il doit être vrai, mais ils sont tous les deux faux.

La raison en est qu'un contrôle d'égalité ==et des comparaisons > < >= <=fonctionnent différemment. Les comparaisons convertissent null en nombre, le traitant comme 0. C'est pourquoi (3) null >= 0est trueet (1) null > 0est false.

D'autre part, le contrôle de l' égalité ==pour undefinedet nullest définie de telle sorte que, sans aucune conversion, ils sont égaux entre eux et ne sont pas égaux quoi que ce soit d' autre. C'est pourquoi (2) null == 0est false.

S. Hesam
la source