作者:Abinav Seelan 原文链接:blog.campvanilla.com/javascript-…
在掘金前端群里(添加掘金酱:juejinjiang 入群)问了大家一个问题:
null > 0
为false
,null == 0
为false
,为什么null>=0
为true
?
有说 ==
不会尝试转型,所以 null == 0
成立。但我记得不转型的是===
啊。
也有说转换为布尔值都为假,那'' == 0
?
还有位群友给出了Number(null)==0
的写法,让表达式结果为 true
。
但是大家都没有正面回答问题。
打开你的浏览器,按下f12,在 console 中输入
null > 0;
null == 0;
null >= 0;复制代码
看看结果是什么?null >0 // false
null == 0// false
null >= 0 // true
神奇吗?一个值既不大于0
,也不等于0
,但它居然大于或等于0
。
一定是我的打开方式有问题,换个浏览器试试!
算了,还是查查 ECMAScript 标准吧。
抽象关系比较算法
先看看
null > 0// false复制代码
根据标准,>
<
操作符利用抽象关系比较算法判断表达式为true
或者false
。
- 调用
ToPrimitive(X, hint Number)
。- 调用
ToPrimitive(y, hint Number)
。- 如果
Type(Result(1))
是字符串且Type(Result(2)
是字符串,跳转到步骤16。- 调用
ToNumber(Result(1))
。- 调用
ToNumber(Result(2))
。- 如果
Result(4)
是 NaN, 返回undefined
。- 如果
Result(5)
是 NaN, 返回undefined
。- 如果
Result(4)
和Result(5)
是同样的数字,返回false
。- 如果
Result(4)
是+0
而且Result(5)
是-0
,返回false
。- 如果
Result(4)
是-0
并且Result(5)
是+0
,返回false
。- 如果
Result(4)
是+∞
,返回false
。- 如果
Result(5)
是+∞
,返回true
。- 如果
Result(5)
是-∞
,返回false
。- 如果
Result(5)
是-∞
,返回true
。- 如果
Result(4)
数学意义上的值小于Result
数学意义上的值——注意这些值都是有穷的且不都为0——返回true
。否则,返回false
。- 如果
Result(2)
是Result(1)
的前缀,返回false
(如果字符串 q 可以由 字符串 p 后跟另一个字符串 r 连接组成,那么字符串 p 就是字符串 q 的前缀。注意任何一个字符串都是其本身的前缀,因为字符串 r 可以是空字符串)。- 如果
Result(1)
是Result(2)
的前缀,返回true
。- 让 k 是最小的非负整数,这样
Result(1)
在 k 位置的字符和Result(2)
在 k 位置的字符会不一样。(因为两个字符串都不是另一个字符串的前缀,所以一定存在这个 k)- 让 m 是
Result(1)
中 k 位置字符的编码值。- 让 n 是
Result(2)
中 k 位置字符的编码值。
21.如果m<n
,返回true
。否则,返回false
。
将null>0
在整个算法中过一遍。
步骤一与步骤二 在null
和0
上调用ToPrimitive()
分别将这两个值转换为原始类型(比如Number
和String
)。ToPrimitive
的结果如下表:
Input type | Result |
---|---|
Undefined | 不转换 |
Null | 不转换 |
Boolean | 不转换 |
Number | 不转换 |
String | 不转换 |
Object | 转换为对象的默认值。对象的默认值通过调用内部的[[DefaultValue]]方法获得,忽略 hint 参数。 |
根据上表,null
和0
都没有转换。
所以步骤三对我们就不适用了,跳过步骤三。步骤四与步骤五需要将左右值均转换为Number
类型。Number
转换规则如下:
Input Type | Result |
---|---|
Undefined | NaN |
Null | +0 |
Number | 不转换 |
Boolean | true 转为1 ,false 转为 +0 |
... | ... |
(String
及 Object
的转换省掉了,因为暂时用不上。如果你好奇可以看这个?)
null
转换为+0
而0
还是0
。两个值都不是NaN
,所以步骤六与步骤七可以跳过。步骤八要注意下,+0
等于0
,所以算法返回false
。所以,
null > 0;//false
and
null < 0;//alse false复制代码
抽象相等比较算法
现在再来看看,null == 0//false
这也非常有趣。
==
操作符利用抽象相等比较算法判断true
或者false
。
- 如果
Type(x)
与Type(y)
不一致,跳转至步骤十四。- 如果
Type(x)
是undefined
, 返回true
。- 如果
Type(x)
是Null
,返回true
。- 如果
Type(x)
不是数字,跳转到步骤十一。- 如果
x
是NaN
, 返回false
。- 如果
y
是NaN
, 返回false
。- 如果
x
和y
是同一个数,返回true
。- 如果
x
是+0
且y
是-0
,返回true
。- 如果
x
是-0
且y
是+0
,返回true
。- 返回
false
。- 如果
Type(x)
是字符串,当x
与y
是完全相同的序列时(长度一致,对应位置的字符也一样。)返回true
。否则返回false
。- 如果
Type(x)
是布尔值,当x
与y
都为true
或都为false
时,语句返回true
,否则返回false
。- 当
x
与y
指向同一个对象或指向的两个对象是联合对象(参见 13.1.2)时,返回true
,否则返回false
。- 如果
x
是null
且y
是undefined
,返回true
。- 如果
x
是undefined
且y
是null
,返回true
。- 如果
Type(x)
是Number
且Type(y)
是字符串,返回x == [ToNumber](http://interglacial.com/javascript_spec/a-9.html#a-9.3)(y)
的结果。- 如果
Type(x)
是String
且Type(y)
是数字,返回[ToNumber](http://interglacial.com/javascript_spec/a-9.html#a-9.3)(x)==y
的结果。- 如果
Type(x)
是布尔值,返回ToNumber(x) == y
的结果。- 如果
Type(y)
是布尔值,返回x ==ToNumber(y)
的结果。- 如果
Type(x)
既不是字符串也不是数字而Type(y)
是对象,返回x == ToPrimitive(y)
的结果。- 如果
Type(x)
是对象而Type(y)
是字符串或数字,返回ToPrimitive(x) == y
。- 返回
false
。
判断 null
与 0
是否相等,我们立刻从步骤一跳到步骤十四。因为 Type 不一样。而且,因为Type(x) 是 null,所以步骤十四到步骤二十一也不适用。最终,步骤二十二默认返回 false。
所以,null == 0 ; //false
大于等于操作符(>=)
现在,我们来看最后一个表达式。null > 0;// true
这一块标准完全把我整蒙了。从宏观看,>=
等价于
如果
null < 0
是false
,那么null>=0
是true
。
所以,null >= 0; //true
老实说,这是有道理的。数学上讲,如果两个数字x
和y
,x
不小于 y
,那么x
必须大于或等于y
。
我猜这么做的目的是为了优化比较表达式。如果可以一次比较——比较x
小于y
是否成立,用这个结果推出原始表达式的结果。那干嘛还要去先比较x
是不是大于y
,如果不是,再比较x
是不是等于y
呢。
(如果你对>=
操作符的实际运算步骤感兴趣,可以看这个)
在探索这个问题的过程中,对这门语言又有了更深的认识。希望这篇文章可以帮助你。