JS 的隐式转换给我们带来了不少便利,然而在一些时候,过度的使用也会导致一些不可预料的问题,比如 1 + '1' 的结果,这是一类经常要注意的简单问题。隐式转换属于类型转换的一部分,类型转换是一个简单而又繁杂的话题。这篇文章旨在理清楚其中的一个相关操作 相等运算符。
相等运算其中包括抽象相等(==)和严格相等(===),它们之间的区别仅在与,严格相等会直接比较类型,而不会做一些隐式转换。其实所谓的隐式转换,只是 EMCA 中规定的一些操作逻辑而已,今天,我们就深入其中,看看这层转换的外壳之下,究竟包含这什么。
相等运算符转换规则
比较 x == y,x 和 y 都是值,返回 true 或者 false。一个比较会执行如下步骤:
注: Type[x] 理解为 x 的类型。ToNumber 和 ToPrimitive 是类型转换中的一种处理,后面会提到。
- 如果
Type[x]和Type[y]的类型相同,那么执行严格相等比较。(所以说,可以理解为===是==的步骤之一) - 如果
x是 null 并且y是 undefined,那么返回 true。 - 如果
x是 undefined 并且y是 undefined,那么返回 true。 - 如果
Type[x]是 Number 并且Type[y]是 String,返回x == !ToNumber(y)的比较结果。 - 如果
Type[x]是 String 并且Type[y]是 Number,返回!ToNumber(x) == y的比较结果。 - 如果
Type[x]是 Boolean,返回!ToNumber(x) == y的比较结果。 - 如果
Type[y]是 Boolean,返回x == !ToNumber(y)的比较结果。 - 如果
Type[x]是 String,Number 或者 Symbol 并且Type[y]是 Object,返回x == ToPrimitive[y]的比较结果。 - 如果
Type[x]是 Object并且Type[y]是 String,Number 或者 Symbol,返回ToPrimitive[x] == y的比较结果。 - 返回 false
化繁为简
上面的 10 条内容看起来头头是道,可是用来记忆就有诸多不便了。总结如下:
类型相同比较值,
null,undefined 总为 true。
Number,String,ToNumber,
若有 Boolean 也 ToNumber。
Object 要 ToPrimitive,
其他结果返回 false。
规则搞清楚以后,只需要弄明白 ToNumber 和 ToPrimitive 是什么就万事大吉了。我们先从简单的说起,
ToNumber 的规则,官方给出一个表格:
| 参数类型 | 结果 |
|---|---|
| Undefined | 返回 NaN |
| Null | 返回 +0 |
| Boolean | 如果 argument 是 true,返回 1,否则返回 0 |
| Number | 返回 argument 没有转换 |
| String | 参考下面的转换算法 |
| Symbol | 抛出类型错误 |
| Object | 两步走: 1. 转换成基本值, ToPrimitive(argument, hint Number)2. 返回 ToNumber() |
好了,到此为止,似乎对 Obejct 的比较运算处理起来需要多一步先转换为数字(也只能这样),再跟具体 ToNumber 比较,由此我们可以进一步推测,在处理字符串,布尔值等基本数据类型的时候,应该会有 ToBoolean 或者 ToString 等等的内置工具(实际上类型转换的规则更多具体,ECMA 中有很详细的介绍)。这里要挖个坑,关于上图表格中的字符串转换算法,我有点看不懂 ECMA 中的介绍,需要继续研究。
关于 ToPrimitive 的内容也很简单,断言处理输入值,然后根据输入类型做不同处理得到基本值。