本期精读的文章是：[null >= 0?](https://blog.campvanilla.com/javascript-the-curious-case-of-null-0-7b131644e274)

# 1 引言

<img src="assets/24/gt.jpeg" width="500" alt="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)，每周都有新的主题，每周五发布。
