¿Por qué `null> = 0 && null <= 0` pero no` null == 0`?

142

Tuve que escribir una rutina que incremente el valor de una variable en 1 si su tipo es numbery asigna 0 a la variable si no, donde la variable está inicialmente nullo undefined.

La primera implementación fue v >= 0 ? v += 1 : v = 0porque pensé que cualquier cosa que no fuera un número haría que una expresión aritmética fuera falsa, pero estaba mal ya que null >= 0se evalúa como verdadera. Luego aprendí nullcomportamientos como 0 y las siguientes expresiones se evalúan como verdaderas.

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

Por supuesto, nullno null == 0es 0. se evalúa como falso. Esto hace que la expresión aparentemente tautológica sea (v >= 0 && v <= 0) === (v == 0)falsa.

¿Por qué es nullcomo 0, aunque en realidad no es 0?

C. Lee
fuente
3
Él está hablando de Javascript. Tu ejemplo está en PHP. En PHP, el operador == compara los valores de una manera especial. Puede hacer algunas comparaciones realmente locas como "10" == "1e1" (lo cual es cierto). Si usó operator ===, obtendría un resultado completamente diferente porque verifica si el tipo coincide y el valor. Mira este enlace: php.net/manual/en/language.operators.comparison.php
Pijusn
El operador PHP '==' realmente funciona de manera "especial".
Two-Bit Alchemist
Si su requerimiento era para empezar a contar a 1 en lugar de 0, hay una manera muy concisa a incrementan los contadores que son inicialmente ya sea nullo undefined:c = -~c // Results in 1 for null/undefined; increments if already a number
Ates Goral
1
undefinedes un valor variable, para variables que no se han inicializado. null, por otro lado, es un valor de objeto vacío y no debe mezclarse con números. nullno debe combinarse con números, por lo que null no debería tener que comportarse como números.
Mateo
1
@AtesGoral: conciso, pero no obvio. Vale la pena recordarle a la gente que cada vez que haga algo no obvio, agregue un comentario que explique lo que hace el código. En la mayoría de las situaciones, lo consideraría una "optimización prematura", dado que cambia la claridad por un aumento de rendimiento minúsculo.
ToolmakerSteve

Respuestas:

207

Su verdadera pregunta parece ser:

Por qué:

null >= 0; // true

Pero:

null == 0; // false

Lo que realmente sucede es que el Operador Mayor-o-igual ( >=), realiza la coerción de tipo ( ToPrimitive), con un tipo de sugerencia de Number, en realidad todos los operadores relacionales tienen este comportamiento.

nulles tratado de manera especial por el operador Equals ( ==). En resumen, solo obliga a undefined:

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

Valor tales como false, '', '0', y []están sujetos a la coerción de tipo numérico, todos ellos coaccionar a cero.

Puede ver los detalles internos de este proceso en El algoritmo de comparación de igualdad abstracta y el Algoritmo de comparación relacional abstracta .

En resumen:

  • Comparación relacional: si ambos valores no son de tipo String, ToNumberse llama a ambos. Esto es lo mismo que agregar un +frente, que para coacciones nulas 0.

  • Comparación de igualdad: solo invoca ToNumbercadenas, números y booleanos.

CMS
fuente
1
Hola CMS, según su explicación, la primitiva nula es 0, por lo que 0> = 0 devuelve verdadero y == devuelve falso. Pero según el algoritmo ecma Si Tipo (x) es Objeto y Tipo (y) es Cadena o Número, devuelve el resultado de la comparación ToPrimitive (x) == y. luego en esto debería devolver true.por
favor explícame
para mí, la respuesta no proporciona una respuesta , null is treated in a special way by the Equals Operator (==). In a brief, it only coerces to undefined:¿y qué? ¿Puedes explicar por qué null >= 0? :)
Andrey Deineko
@bharathmuppa @ andrey-deineko: El resto de la respuesta de CMS está aquí: El algoritmo de comparación relacional abstracta que explica en el punto 3. que si ambos valores no son de tipo String, ToNumber se llama a ambos. Esto es lo mismo que agregar un +frente, que para coacciones nulas 0. Igualdad solo llama a ToNumber en cadenas, números y booleanos.
Michael Liquori
77
Buena descripción, pero no me gusta. En cualquier idioma (x == 0 || x> 0) debe ser equivalente a (x> = 0). JavaScript es un lenguaje estúpido.
John Henckel
1
Es simplemente un error en la especificación realmente (porque matemáticamente está mal) y no hay nada que hacer al respecto ya que millones de sitios web se basan en comparaciones nulas ^^ '
mahieddine
14

Me gustaría extender la pregunta para mejorar aún más la visibilidad del problema:

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

Simplemente no tiene sentido. Al igual que los idiomas humanos, estas cosas deben aprenderse de memoria.

estani
fuente
1
Como se describió anteriormente, puede explicarse con solo una excepción, ya que == trata nulo, de lo contrario, en todos los casos, nulo se convierte en 0 usando Número (nulll)
Sourabh Ranka
5

JavaScript tiene comparaciones estrictas y de conversión de tipos

null >= 0;es cierto pero (null==0)||(null>0)es falso

null <= 0;es cierto pero (null==0)||(null<0)es falso

"" >= 0 también es cierto

Para las comparaciones abstractas relacionales (<=,> =), los operandos se convierten primero en primitivos, luego en el mismo tipo, antes de la comparación.

typeof null returns "object"

Cuando type es object, JavaScript intenta stringificar el objeto (es decir, nulo), se siguen los siguientes pasos ( ECMAScript 2015 ):

  1. Si PreferredTypeno se aprobó, deje que hintsea ​​"predeterminado".
  2. De lo contrario, si PreferredTypees hintString, deja hintser "string".
  3. De lo contrario, PreferredTypees hintNúmero, seamos hint"número".
  4. Dejar que exoticToPrimsea GetMethod(input, @@toPrimitive).
  5. ReturnIfAbrupt(exoticToPrim).
  6. Si exoticToPrimno está indefinido, entonces
    a) Sea el resultado Call(exoticToPrim, input, «hint»).
    b) ReturnIfAbrupt(result).
    c) Si Type(result)no es Objeto, devuelve el resultado.
    d) Lanzar una excepción TypeError.
  7. Si hintes "predeterminado", hintsea ​​"número".
  8. Retorno OrdinaryToPrimitive(input,hint).

Los valores permitidos para la sugerencia son "predeterminado", "número" y "cadena". Los objetos de fecha son únicos entre los objetos ECMAScript integrados en que tratan "predeterminado" como equivalente a "cadena". Todos los demás objetos ECMAScript integrados tratan "predeterminado" como equivalente a "número" . ( ECMAScript 20.3.4.45 )

Entonces creo que se nullconvierte a 0.

Panos Kal.
fuente
1

Yo tuve el mismo problema !!. Actualmente mi única solución es separarme.

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
fuente
Puede ser que sea más claro para hacer lugar: if (a!=null && a>=0). Esto aclara la razón de no hacerlo simplemente >=por sí mismo: "a podría ser nulo (o indefinido, que también es '== nulo')".
ToolmakerSteve
0
console.log( null > 0 );  // (1) false
console.log( null == 0 ); // (2) false
console.log( null >= 0 ); // (3) true

Matemáticamente, eso es extraño. El último resultado establece que "nulo es mayor o igual que cero", por lo que en una de las comparaciones anteriores debe ser cierto, pero ambos son falsos.

La razón es que un control de igualdad ==y comparaciones > < >= <=funcionan de manera diferente. Las comparaciones convierten nulo en un número, tratándolo como 0. Es por eso que (3) null >= 0es truey (1) null > 0es false.

Por otro lado, la verificación de igualdad ==para undefinedy nullse define de tal manera que, sin ninguna conversión, se igualen entre sí y no igualen nada más. Es por eso que (2) null == 0es false.

S. Hesam
fuente