Dlaczego `null> = 0 && null <= 0`, ale nie` null == 0`?

142

Musiałem napisać procedurę, która zwiększa wartość zmiennej o 1, jeśli jej typ to numberi przypisuje 0 do zmiennej, jeśli nie, gdzie zmienna jest początkowo nulllub undefined.

Pierwsza implementacja była v >= 0 ? v += 1 : v = 0taka, ponieważ myślałem, że cokolwiek, co nie jest liczbą, spowoduje, że wyrażenie arytmetyczne będzie fałszywe, ale było błędne, ponieważ null >= 0jest oceniane jako prawda. Potem dowiedziałem nullsię, że zachowuje się jak 0 i wszystkie poniższe wyrażenia są oceniane jako prawdziwe.

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

Oczywiście nullnie null == 0jest 0 jest oceniane jako fałsz. To sprawia, że ​​pozornie tautologiczne wyrażenie jest (v >= 0 && v <= 0) === (v == 0)fałszywe.

Dlaczego jest nulljak 0, chociaż w rzeczywistości nie jest to 0?

C. Lee
źródło
3
Mówi o Javascript. Twój przykład jest w PHP. W PHP operator == porównuje wartości w specjalny sposób. Możesz dokonać naprawdę szalonych porównań, takich jak „10” == „1e1” (co jest prawdą). Jeśli użyjesz operatora ===, otrzymasz zupełnie inny wynik, ponieważ sprawdza, czy typ jest zgodny z wartością. Sprawdź ten link: php.net/manual/en/language.operators.comparison.php
Pijusn
Operator PHP '==' naprawdę działa w „specjalny” sposób.
Two-Bit Alchemist
Jeśli wymaganie było rozpoczęcie liczenia na 1 zamiast 0, istnieje bardzo lakoniczny sposób liczników przyrostu że są początkowo albo nullalbo undefined:c = -~c // Results in 1 for null/undefined; increments if already a number
Ates Goral
1
undefinedjest wartością zmiennej dla zmiennych, które nie zostały zainicjowane. nullz drugiej strony jest pustą wartością obiektu i nie należy jej mieszać z liczbami. nullnie powinno być łączone z liczbami, więc null nie powinno zachowywać się jak liczby.
Matthew
1
@AtesGoral - zwięzłe, ale nieoczywiste. Warto przypomnieć ludziom, że robiąc coś nieoczywistego, prosimy o dodanie komentarza wyjaśniającego, co robi kod. W większości sytuacji uznałbym to za „przedwczesną optymalizację”, biorąc pod uwagę, że zapewnia przejrzystość w zamian za niewielki wzrost wydajności.
ToolmakerSteve

Odpowiedzi:

207

Twoje prawdziwe pytanie brzmi:

Czemu:

null >= 0; // true

Ale:

null == 0; // false

To, co naprawdę się dzieje, to fakt, że operator Większy lub równy ( >=) wykonuje wymuszenie typu ( ToPrimitive), z podpowiedzią typu Number, w rzeczywistości wszystkie operatory relacyjne mają takie zachowanie.

nulljest traktowany w specjalny sposób przez operatora równości ( ==). Krótko mówiąc, zmusza tylko do undefined:

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

Wartość takich jak false, '', '0', i []podlegają numerycznej typu przymus, wszystkie z nich przymusić do zera.

Możesz zobaczyć wewnętrzne szczegóły tego procesu w algorytmie abstrakcyjnego porównania równości i algorytmu abstrakcyjnego porównania relacyjnego .

W podsumowaniu:

  • Porównanie relacyjne: jeśli obie wartości nie są typu String, ToNumberjest wywoływane na obu. To jest to samo, co dodanie z +przodu, co dla zerowych wymuszeń 0.

  • Porównanie równości: wywołuje tylko ToNumberłańcuchy, liczby i wartości logiczne.

CMS
źródło
1
Cześć CMS, zgodnie z twoim wyjaśnieniem null prymityw to 0, więc 0> = 0 zwraca prawdę, a == zwraca fałsz. Ale zgodnie z algorytmem ecma Jeśli Type (x) to Object, a Type (y) to String lub Number, zwróć wynik porównania ToPrimitive (x) == y. wtedy w tym powinno zwrócić prawdę. proszę wyjaśnić
bharath muppa
mi odpowiedź nie daje odpowiedzi - null is treated in a special way by the Equals Operator (==). In a brief, it only coerces to undefined:- a co? Czy możesz wyjaśnić, dlaczego null >= 0? :)
Andrey Deineko
@bharathmuppa @ andrey-deineko: Reszta odpowiedzi CMS jest następująca: Algorytm abstrakcyjnego porównania relacyjnego, który wyjaśnia w punkcie 3., że jeśli obie wartości nie są typu String, na obu jest wywoływana ToNumber. To jest to samo, co dodanie z +przodu, co dla zerowych wymuszeń 0. Równość wywołuje tylko ToNumber na ciągach, liczbach i logicznych.
Michael Liquori
7
Dobry opis, ale mi się to nie podoba. W każdym języku (x == 0 || x> 0) powinno być równoważne (x> = 0). javascript to głupi język.
John Henckel
1
To po prostu błąd w specyfikacji (ponieważ matematycznie jest zły) i nie ma nic do zrobienia, ponieważ miliony witryn internetowych opierają się na zerowych porównaniach ^^ '
mahieddine
14

Chciałbym rozszerzyć pytanie, aby jeszcze bardziej poprawić widoczność problemu:

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

To po prostu nie ma sensu. Podobnie jak w przypadku ludzkich języków, tych rzeczy trzeba się uczyć na pamięć.

estani
źródło
1
Jak opisano powyżej, można to wyjaśnić tylko z wyjątkiem tego, jak == traktuje wartość null, w przeciwnym razie we wszystkich przypadkach wartość null jest konwertowana na 0 za pomocą Number (nulll)
Sourabh Ranka
5

JavaScript oferuje zarówno ścisłe, jak i konwertujące typy porównań

null >= 0;jest prawdziwe, ale (null==0)||(null>0)jest fałszywe

null <= 0;jest prawdziwe, ale (null==0)||(null<0)jest fałszywe

"" >= 0 jest również prawdą

W przypadku relacyjnych porównań abstrakcyjnych (<=,> =) operandy są najpierw konwertowane na prymitywy, a następnie na ten sam typ, przed porównaniem.

typeof null returns "object"

Gdy typ jest obiektem, javascript próbuje nadać obiektowi ciąg znaków (tj. Null), podejmowane są następujące kroki ( ECMAScript 2015 ):

  1. Jeśli PreferredTypenie został przekazany, niech hintbędzie „domyślny”.
  2. W przeciwnym razie, jeśli PreferredTypejest ciągiem hint, niech hintbędzie „ciągiem”.
  3. Inny PreferredTypejest hintnumer, niech hintbędzie „numer”.
  4. Niech exoticToPrimbędzie GetMethod(input, @@toPrimitive).
  5. ReturnIfAbrupt(exoticToPrim).
  6. Jeśli exoticToPrimnie jest niezdefiniowane, to
    a) Niech wynik będzie Call(exoticToPrim, input, «hint»).
    b) ReturnIfAbrupt(result).
    c) Jeśli Type(result)nie jest Object, zwraca wynik.
    d) Zgłoś wyjątek TypeError.
  7. Jeśli hintjest „domyślna”, niech hintbędzie „liczbą”.
  8. Wróć OrdinaryToPrimitive(input,hint).

Dozwolone wartości podpowiedzi to „default”, „number” i „string”. Obiekty Date są unikalne wśród wbudowanych obiektów ECMAScript, ponieważ traktują „default” jako odpowiednik „string”. Wszystkie inne wbudowane obiekty ECMAScript traktują „default” jako odpowiednik „number” . ( ECMAScript 20.3.4.45 )

Więc myślę, że nullkonwertuje do 0.

Panos Kal.
źródło
1

Miałem ten sam problem !!. Obecnie moim jedynym rozwiązaniem jest oddzielenie.

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
źródło
To może być jaśniejsze zamiast zrobić: if (a!=null && a>=0). To wyjaśnia powód, dla którego nie robisz >=tego samodzielnie: „a może być zerowe (lub niezdefiniowane, co oznacza również '== null')”.
ToolmakerSteve
0
console.log( null > 0 );  // (1) false
console.log( null == 0 ); // (2) false
console.log( null >= 0 ); // (3) true

Matematycznie to dziwne. Ostatni wynik stwierdza, że ​​„null jest większe lub równe zero”, więc w jednym z powyższych porównań musi to być prawda, ale oba są fałszywe.

Powodem jest to, że sprawdzanie równości ==i porównania > < >= <=działają inaczej. Porównania konwertują wartość null na liczbę, traktując ją jako 0. Dlatego (3) null >= 0jest, truea (1) null > 0jest false.

Z drugiej strony, sprawdzenie równości ==dla undefineda nulljest zdefiniowana tak, że bez żadnej konwersji, to równa się wzajemnie i nie robić nic więcej równe. Dlatego (2) null == 0jest false.

S. Hesam
źródło