本期精读的文章是:[null >= 0?](https://blog.campvanilla.com/javascript-the-curious-case-of-null-0-7b131644e274) # 1 引言 logo 你是如何看待 null >= 0 为 `true` 这个结果的呢?要么选择勉强接受,要么跟着我一探究竟吧。 # 2 内容概要 ## 大于判断 javascript 在判断 `a > b` 时,记住下面 21 步判断法: 1. 调用 b 的 ToPrimitive(hit Number) 方法. 2. 调用 a 的 ToPrimitive(hit Number) 方法. 3. 如果此时 Result(1) 与 Result(2) 都是字符串,跳到步骤 16. 4. 调用 ToNumber(Result(1)). 5. 调用 ToNumber(Result(2)). 6. 如果 Result(4) 为 NaN, return undefined. 7. 如果 Result(5) 为 NaN, return undefined. 8. 如果 Result(4) 和 Result(5) 是相同的数字,return false. 9. 如果 Result(4) 为 +0, Result(5) 为 -0, return false. 10. 如果 Result(4) 为 -0, Result(5) 为 +0, return false. 11. 如果 Result(4) 为 +∞, return false. 12. 如果 Result(5) 为 +∞, return true. 13. 如果 Result(5) 为 -∞, return false. 14. 如果 Result(4) 为 -∞, return true. 15. 如果 Result(4) 的数值大小小于 Result(5),return true,否则 return false. 16. 如果 Result(2) 是 Result(1) 的前缀 return false. (比如 "ab" 是 "abc" 的前缀) 17. 如果 Result(1) 是 Result(2) 的前缀, return true. 18. 找到一个位置 k,使得 a[k] 与 b[k] 不相等. 19. 取 m 为 a[k] 字符的数值. 20. 取 n 为 b[k] 字符的数值. 21. 如果 m < n, return true,否则 return false. > ToPrimitive 会按照顺序优先使用存在的值:valueOf()、toString(),如果都没有,会抛出异常。 > ToPrimitive(hit Number) 表示隐转数值类型 所以 null > 0 结果为 `false`。 ## 等于判断 现在看看 `a == b` 时的表现(三等号会严格判断类型,两等号反而是最复杂的情况)。 1. 如果 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. 2. 如果 a 为 null,b 为 undefined,return true. 3. 如果 a 为 undefined,b 为 null,return true. 4. 如果 Type(a) 为 number,Type(b) 为 string,返回 a == ToNumber(b) 的结果. 5. 如果 Type(a) 为 string,Type(b) 为 number,返回 ToNumber(a) == b 的结果. 6. 如果 Type(a) 为 boolean,返回 ToNumber(a) == b 的结果. 7. 如果 Type(b) 为 boolean,返回 a == ToNumber(b) 的结果. 8. 如果 Type(a) 是 string 或 number,且 Type(b) 是对象类型,返回 a == ToPrimitive(b) 的结果. 9. 如果 Type(a) 是对象类型,且 Type(b) 是 string 或 number,返回 ToPrimitive(a) == b 的结果. 10. 否则 return false. 所以 null == 0 走到了第 10 步,返回了默认的 `false`。 ## 大于等于判断 javascript 是这么定义大于等于判断的: > 如果 a < b 为 `false`,则 a >= b 为 `true` 所以 null >= 0 为 `true`,因为 null < 0 是 `false`. # 3 精读 ### 关于 toPrimitive 拓展一下,我们可以通过 `Symbol.toPrimitive` 定义某个 class 的 ToPrimitive 行为,比如: ```javascript class AnswerToLifeAndUniverseAndEverything { [Symbol.toPrimitive](hint) { if (hint === 'string') { return 'Like, 42, man'; } else if (hint === 'number') { return 42; } else { // when pushed, most classes (except Date) // default to returning a number primitive return 42; } } } ``` ### 还有不按套路出牌的情况? 按上面的道理,我们可以举一反三: ```javascript {} >= {} // true ``` 可是这是为何呢? ```javascript null >= {} // false ``` 仔细读过上文应该不难发现,如果 ToPrimitive(hit Number) 出现了 NaN,将直接 return undefined,也就是打印出 false,而下面是隐式转换表,{} 的结果是 NaN,因此结果是 false。 ![primitive](https://camo.githubusercontent.com/c8ccc486bd441453d9c3529ed6d3b26661541787/68747470733a2f2f692e6c6f6c692e6e65742f323031372f30392f32322f353963346362316238336434632e706e67) # 4 总结 NaN 在 javascript 是个特殊存在,只有 `isNaN` 可以准确判断到它,而且使用它进行比较判断时,会直接 return false. javascript 隐式转换有一套优先级规则,而且不同值的隐式转换还需要对照表记忆,还存在 `ToPrimitive(hint Number)` `ToPrimitive(hint String)` `ToPrimitive(hint Boolean)` 三份表,记忆起来确实有点复杂。 因此推荐比较判断时,尽量使用 `===`,通过 `Typescript` `Flow` 等强类型语言约束变量类型,尽量不要做不同类型变量间的比较。 > 讨论地址是:[精读《null >= 0?》 · Issue #36 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/36) > 如果你想参与讨论,请[点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,每周五发布。