前言
javaScript中有很多异于常人思维的逻辑,比如null > 0, null == 0都为false,但null >= 0 却为true。
有些人看到这里觉得这怎么可能,于是跑到浏览器控制台尝试执行了一番,执行后的结果,让自己大吃一惊。
心想自己可能因为这个写了不少bug,今天又学到了一个知识点,但这你要不去了解它的执行原理,估计你怎么都想不通。
下面我们就尝试去一探究竟吧!
ToPrimitive 算法
JavaScript 对象转换到基本类型值时,会使用 ToPrimitive 算法,这是一个内部算法,是编程语言在内部执行时遵循的一套规则。
hint
ToPrimitive 算法在执行时,会被传递一个参数 hint,表示这是一个什么类型的运算(也可以叫运算的期望值),根据这个 hint 参数,ToPrimitive 算法来决定内部的执行逻辑。
hint 参数的取值只能是下列 3 者之一:
- string
- number
- default
toPrimitive转换规则
「如果传入参数是string,也就是对象到字符串的转换」,经过了如下步骤:
- 如果对象中有toString()方法,则调用这个方法。如果它返回一个原始值(undefined、Boolean、Number、String、BigInt、Symbol 和 null),js将这个值转换为字符串(如果本身不是字符串的话),并返回这个字符串结果。
- 如果对象没有toString()方法,或者toString()没有返回一个原始值,那么js会调用valueOf()方法。如果返回值是原始值,js将这个值转换为字符串,并返回字符串结果。
- 否则,js抛出一个类型错误异常。
「如果传入参数是number/default,也就是对象到数字的转换」,经过了如下步骤:
和上面有点不同,到数字的转换会先尝试使用valueOf()方法
- 如果对象具有valueOf()方法,后者返回一个原始值,则js会将其转换为数字(如果需要的话)并返回这个数字。
- 否则,如果对象具有toString()方法,返回一个原始值(字符串直接量),则js将其转换为数字类型,并返回这个数字。
- 否则,js抛出一个类型错误异常。
抽象关系比较算法
- 调用 b 的 ToPrimitive(hit Number) 方法.
- 调用 a 的 ToPrimitive(hit Number) 方法.
- 如果此时 Result(1) 与 Result(2) 都是字符串,跳到步骤 16.
- 调用 ToNumber(Result(1)).
- 调用 ToNumber(Result(2)).
- 如果 Result(4) 为 NaN, return undefined.
- 如果 Result(5) 为 NaN, return undefined.
- 如果 Result(4) 和 Result(5) 是相同的数字,return false.
- 如果 Result(4) 为 +0, Result(5) 为 -0, return false.
- 如果 Result(4) 为 -0, Result(5) 为 +0, return false.
- 如果 Result(4) 为 +∞, return false.
- 如果 Result(5) 为 +∞, return true.
- 如果 Result(5) 为 -∞, return false.
- 如果 Result(4) 为 -∞, return true.
- 如果 Result(4) 的数值大小小于 Result(5),return true,否则 return false.
- 如果 Result(2) 是 Result(1) 的前缀 return false. (比如 "ab" 是 "abc" 的前缀)
- 如果 Result(1) 是 Result(2) 的前缀, return true.
- 找到一个位置 k,使得 a[k] 与 b[k] 不相等.
- 取 m 为 a[k] 字符的数值.
- 取 n 为 b[k] 字符的数值.
- 如果 m < n, return true,否则 return false.
判断null>0
按照上面这个步骤,我们可以尝试来判断一下null>0的结果
首先第一二步就是为它们分别调用ToPrimitive()将这两个值转换为原始类型,由于这两个值都是基本类型,所以他们转换后还是本身
然后第三步就不适用,我们接着看第四五步,将两个值都转为Number类型,null转换成了+0,而0还是0。
接着看六七,由于两者都不是NaN,所以我们直接看第八步,在js中+0与0是一样的,所以返回false
null > 0 // false
null < 0 // false
抽象相等比较算法
- 如果 a 与 b 的类型相同,则:
- 如果 Type(b) 为 undefined,return true.
- 如果 Type(b) 为 null,return true.
- 如果 Type(b) 为 number,则:
- 如果 b 为 NaN,return false.
- 如果 a 为 NaN,return false.
- 如果 a 与 b 数值相同,return true.
- 如果 a 为 +0,b 为 -0,return true.
- 如果 a 为 -0,b 为 +0,return true.
- 否则 return false.
- 如果 Type(b) 为 string,且 a 与 b 是完全相同的字符串,return true,否则 return false.
- 如果 Type(b) 是 boolean,如果都是 true 或 false,return true,否则 return false.
- 如果 a 与 b 是同一个对象引用,return true,否则 return false.
- 如果 a 为 null,b 为 undefined,return true.
- 如果 a 为 undefined,b 为 null,return true.
- 如果 Type(a) 为 number,Type(b) 为 string,返回 a == ToNumber(b) 的结果.
- 如果 Type(a) 为 string,Type(b) 为 number,返回 ToNumber(a) == b 的结果.
- 如果 Type(a) 为 boolean,返回 ToNumber(a) == b 的结果.
- 如果 Type(b) 为 boolean,返回 a == ToNumber(b) 的结果.
- 如果 Type(a) 是 string 或 number,且 Type(b) 是对象类型,返回 a == ToPrimitive(b) 的结果.
- 如果 Type(a) 是对象类型,且 Type(b) 是 string 或 number,返回 ToPrimitive(a) == b 的结果.
- 否则 return false.
判断null==0
null == 0 // false
null == 0 走到了第 10 步,返回了默认的 false。
大于等于操作符>=
从常理上来讲,如果null>0为false,null==0也为false,那么null>=0肯定也为false。但事实并非如此
javascript 是这么定义大于等于判断的:
如果 a < b 为 false,则 a >= b 为 true
这个规则是不是有点逆于常人思维,但它却又是合理的,当a<b为false,那反过来a>=b肯定就为true对吧
所以null>=0为true,是因为null<0为false,看到这里,是不是又恍然大悟了呢?