Why `null >= 0 && null <= 0` but not `null == 0`?
I had to write a routine that increments the value of a variable by 1 if its type is number
and assigns 0 to the variable if not, where the variable is initially null
or undefined
.
The first implementation was v >= 0 ? v += 1 : v = 0
because I thought anything not a number would make an arithmetic expression false, but it was wrong since null >= 0
is evaluated to true. Then I learned null
behaves like 0 and the following expressions are all evaluated to true.
null >= 0 && null <= 0
!(null < 0 || null > 0)
null + 1 === 1
1 / null === Infinity
Math.pow(42, null) === 1
Of course, null
is not 0. null == 0
is evaluated to false. This makes the seemingly tautological expression (v >= 0 && v <= 0) === (v == 0)
false.
Why is null
like 0, although it is not actually 0?
Solution 1:
Your real question seem to be:
Why:
null >= 0; // true
But:
null == 0; // false
What really happens is that the Greater-than-or-equal Operator (>=
), performs type coercion (ToPrimitive
), with a hint type of Number
, actually all the relational operators have this behavior.
null
is treated in a special way by the Equals Operator (==
). In a brief, it only coerces to undefined
:
null == null; // true
null == undefined; // true
Value such as false
, ''
, '0'
, and []
are subject to numeric type coercion, all of them coerce to zero.
You can see the inner details of this process in the The Abstract Equality Comparison Algorithm and The Abstract Relational Comparison Algorithm.
In Summary:
Relational Comparison: if both values are not type String,
ToNumber
is called on both. This is the same as adding a+
in front, which for null coerces to0
.Equality Comparison: only calls
ToNumber
on Strings, Numbers, and Booleans.
Solution 2:
I'd like to extend the question to further improve visibility of the problem:
null >= 0; //true
null <= 0; //true
null == 0; //false
null > 0; //false
null < 0; //false
It just makes no sense. Like human languages, these things need be learned by heart.
Solution 3:
JavaScript has both strict and type–converting comparisons
null >= 0;
is true
but
(null==0)||(null>0)
is false
null <= 0;
is true but (null==0)||(null<0)
is false
"" >= 0
is also true
For relational abstract comparisons (<= , >=), the operands are first converted to primitives, then to the same type, before comparison.
typeof null returns "object"
When type is object javascript tries to stringify the object (i.e null) the following steps are taken (ECMAScript 2015):
- If
PreferredType
was not passed, lethint
be "default". - Else if
PreferredType
ishint
String, lethint
be "string". - Else
PreferredType
ishint
Number, lethint
be "number". - Let
exoticToPrim
beGetMethod(input, @@toPrimitive)
. -
ReturnIfAbrupt(exoticToPrim)
. - If
exoticToPrim
is not undefined, then
a) Let result beCall(exoticToPrim, input, «hint»)
.
b)ReturnIfAbrupt(result)
.
c) IfType(result)
is not Object, return result.
d) Throw a TypeError exception. - If
hint
is "default", lethint
be "number". - Return
OrdinaryToPrimitive(input,hint)
.
The allowed values for hint are "default", "number", and "string". Date objects, are unique among built-in ECMAScript object in that they treat "default" as being equivalent to "string". All other built-in ECMAScript objects treat "default" as being equivalent to "number". (ECMAScript 20.3.4.45)
So I think null
converts to 0.
Solution 4:
console.log( null > 0 ); // (1) false
console.log( null == 0 ); // (2) false
console.log( null >= 0 ); // (3) true
Mathematically, that’s strange. The last result states that "null is greater than or equal to zero", so in one of the comparisons above it must be true, but they are both false.
The reason is that an equality check ==
and comparisons > < >= <=
work differently. Comparisons convert null to a number, treating it as 0
. That’s why (3) null >= 0
is true
and (1) null > 0
is false
.
On the other hand, the equality check ==
for undefined
and null
is defined such that, without any conversions, they equal each other and don’t equal anything else. That’s why (2) null == 0
is false
.
Solution 5:
I had the same problem !!. Currently my only solution is to separate.
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 !