Warum `null> = 0 && null <= 0`, aber nicht` null == 0`?

142

Ich musste eine Routine schreiben, die den Wert einer Variablen um 1 erhöht, wenn ihr Typ ist, numberund der Variablen 0 zuweist, wenn nicht, wo die Variable anfänglich ist nulloder undefined.

Die erste Implementierung war, v >= 0 ? v += 1 : v = 0weil ich dachte, dass alles, was keine Zahl ist, einen arithmetischen Ausdruck falsch machen würde, aber es war falsch, da null >= 0es als wahr ausgewertet wird. Dann habe ich gelernt, dass es nullsich wie 0 verhält und die folgenden Ausdrücke alle als wahr ausgewertet werden.

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

Natürlich nullist nicht 0. null == 0wird als falsch bewertet. Dies macht den scheinbar tautologischen Ausdruck (v >= 0 && v <= 0) === (v == 0)falsch.

Warum ist nullwie 0, obwohl es eigentlich nicht 0 ist?

C. Lee
quelle
3
Er spricht über Javascript. Ihr Beispiel ist in PHP. Im PHP-Operator == vergleicht Werte auf besondere Weise. Sie können einige wirklich verrückte Vergleiche wie "10" == "1e1" (was wahr ist) machen. Wenn Sie den Operator === verwenden, erhalten Sie ein ganz anderes Ergebnis, da überprüft wird, ob der Typ und der Wert übereinstimmen. Überprüfen Sie diesen Link aus: php.net/manual/en/language.operators.comparison.php
Pijusn
Der PHP-Operator '==' funktioniert wirklich auf "besondere" Weise.
Zwei-Bit-Alchemist
Wenn Ihre Anforderung darin bestand, bei 1 statt bei 0 zu zählen, gibt es eine sehr knappe Möglichkeit, Zähler zu erhöhen, die anfänglich entweder nulloder undefined:c = -~c // Results in 1 for null/undefined; increments if already a number
Ates Goral
1
undefinedist ein Variablenwert für Variablen, die nicht initialisiert wurden. nullist andererseits ein leerer Objektwert und sollte nicht mit Zahlen gemischt werden. nullsollte nicht mit Zahlen kombiniert werden, daher sollte sich null nicht wie Zahlen verhalten müssen.
Matthew
1
@AtesGoral - knapp, aber nicht offensichtlich. Es lohnt sich, die Leute daran zu erinnern, dass sie immer dann, wenn sie etwas Unverständliches tun, einen Kommentar hinzufügen, der erklärt, was der Code tut. In den meisten Situationen würde ich es als "vorzeitige Optimierung" betrachten, da es Klarheit gegen einen winzigen Leistungsgewinn eintauscht.
ToolmakerSteve

Antworten:

207

Ihre eigentliche Frage scheint zu sein:

Warum:

null >= 0; // true

Aber:

null == 0; // false

Was wirklich passiert, ist, dass der Operator "Größer als oder gleich" ( >=) einen Typzwang ( ) ausführt ToPrimitive, mit einem Hinweis vom Typ Number, tatsächlich haben alle relationalen Operatoren dieses Verhalten.

nullwird vom Equals Operator ( ==) in besonderer Weise behandelt . Kurz gesagt , es zwingt nur zu undefined:

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

Wert wie false, '', '0'und []unterliegt numerischen Typ Zwang, alle von ihnen auf Null zwingen.

Sie können die inneren Details dieses Prozesses im abstrakten Gleichheitsvergleichsalgorithmus und im abstrakten relationalen Vergleichsalgorithmus sehen .

Zusammenfassend:

  • Relationaler Vergleich: Wenn beide Werte nicht vom Typ String sind, ToNumberwird für beide aufgerufen. Dies ist das gleiche wie das Hinzufügen eines +Front, das für Null erzwingt 0.

  • Gleichheitsvergleich: ToNumberRuft nur Zeichenfolgen, Zahlen und Boolesche Werte auf.

CMS
quelle
1
Hallo CMS, gemäß Ihrer Erklärung ist null primitiv 0, also gibt 0> = 0 true zurück und == gibt false zurück. Gemäß dem ecma-Algorithmus Wenn Typ (x) Objekt ist und Typ (y) entweder String oder Zahl ist, Geben Sie das Ergebnis des Vergleichs zurück. ToPrimitive (x) == y. Dann sollte es true zurückgeben. Bitte erklären Sie mir
Bharath Muppa
Für mich gibt die Antwort keine Antwort - null is treated in a special way by the Equals Operator (==). In a brief, it only coerces to undefined:- und was? Können Sie erklären, warum null >= 0? :)
Andrey Deineko
@bharathmuppa @ andrey-deineko: Der Rest der Antwort von CMS lautet hier: Der abstrakte relationale Vergleichsalgorithmus, der in Punkt 3 erklärt, dass ToNumber für beide aufgerufen wird, wenn beide Werte nicht vom Typ String sind. Dies ist das gleiche wie das Hinzufügen eines +Front, das für Null erzwingt 0. Equality ruft ToNumber nur für Zeichenfolgen, Zahlen und Boolesche Werte auf.
Michael Liquori
7
Gute Beschreibung, aber ich mag es nicht. In jeder Sprache sollte (x == 0 || x> 0) äquivalent zu (x> = 0) sein. Javascript ist eine dumme Sprache.
John Henckel
1
Es ist einfach ein Fehler in der Spezifikation (weil es mathematisch falsch ist) und es gibt nichts zu tun, da Millionen von Websites auf
Nullvergleiche angewiesen sind
14

Ich möchte die Frage erweitern, um die Sichtbarkeit des Problems weiter zu verbessern:

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

Es macht einfach keinen Sinn. Wie menschliche Sprachen müssen diese Dinge auswendig gelernt werden.

estani
quelle
1
Wie oben beschrieben, kann es mit nur einer Ausnahme erklärt werden, wie == null behandelt, andernfalls wird null in allen Fällen mit Number (nulll) in 0 umgewandelt
Sourabh Ranka
5

JavaScript bietet sowohl strenge als auch typkonvertierende Vergleiche

null >= 0;ist wahr, aber (null==0)||(null>0)falsch

null <= 0;ist wahr, aber (null==0)||(null<0)falsch

"" >= 0 ist auch wahr

Bei relationalen abstrakten Vergleichen (<=,> =) werden die Operanden vor dem Vergleich zunächst in Grundelemente und dann in denselben Typ konvertiert.

typeof null returns "object"

Wenn der Typ Objekt ist, versucht Javascript, das Objekt zu stringifizieren (dh null), werden die folgenden Schritte ausgeführt ( ECMAScript 2015 ):

  1. Wenn PreferredTypenicht bestanden wurde, sei hint"Standard".
  2. Else if PreferredTypeist hintString, lassen hint"string" sein.
  3. Sonst PreferredTypeist hintNummer, sei hint"Nummer".
  4. Lass exoticToPrimsein GetMethod(input, @@toPrimitive).
  5. ReturnIfAbrupt(exoticToPrim).
  6. Wenn exoticToPrimnicht undefiniert ist, dann
    a) Sei das Ergebnis Call(exoticToPrim, input, «hint»).
    b) ReturnIfAbrupt(result).
    c) Wenn Type(result)nicht Object, Ergebnis zurückgeben.
    d) Wirf eine TypeError-Ausnahme aus.
  7. Wenn hint"Standard" ist, sei hint"Nummer".
  8. Zurück OrdinaryToPrimitive(input,hint).

Die zulässigen Werte für den Hinweis sind "Standard", "Nummer" und "Zeichenfolge". Datumsobjekte sind unter den integrierten ECMAScript-Objekten insofern einzigartig, als sie "Standard" als äquivalent zu "Zeichenfolge" behandeln. Alle anderen integrierten ECMAScript-Objekte behandeln "Standard" als gleichwertig mit "Nummer" . ( ECMAScript 20.3.4.45 )

Also ich denke nullkonvertiert in 0.

Panos Kal.
quelle
1

Ich hatte das gleiche Problem !!. Derzeit ist meine einzige Lösung zu trennen.

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
quelle
Es könnte klarer sein, stattdessen Folgendes zu tun : if (a!=null && a>=0). Dies verdeutlicht den Grund dafür, nicht einfach >=von selbst zu tun : "a könnte null sein (oder undefiniert, was auch '== null' ist)".
ToolmakerSteve
0
console.log( null > 0 );  // (1) false
console.log( null == 0 ); // (2) false
console.log( null >= 0 ); // (3) true

Mathematisch ist das seltsam. Das letzte Ergebnis besagt, dass "null größer oder gleich Null ist", daher muss es in einem der obigen Vergleiche wahr sein, aber beide sind falsch.

Der Grund ist, dass eine Gleichstellungsprüfung ==und Vergleiche > < >= <=unterschiedlich funktionieren. Vergleiche wandeln null in eine Zahl um und behandeln sie als 0. Aus diesem Grund (3) null >= 0ist trueund (1) null > 0ist false.

Andererseits wird die Gleichheitsprüfung ==für undefinedund nullso definiert, dass sie sich ohne Konvertierungen gleichen und nichts anderes entsprechen. Aus diesem Grund (2) null == 0ist false.

S. Hesam
quelle