宽松相等(loose equals)再也不迷惑

最近忙着复习找工做,在看书和刷题过程当中,总是被各类有关宽松相等的题卡住。也为此查阅不少博客、书籍,甚至看了一些网上视频,也仍是很迷惑。所以,花了一些时间,查阅了ES标准,瞬间有种醍醐灌顶的感受。javascript

The Abstract Equality Comparison Algorithm(ES标准)

The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:

1. If Type(x) is different from Type(y), go to step 14.
2.If Type(x) is Undefined, return true.
3.If Type(x) is Null, return true.
4.If Type(x) is not Number, go to step 11.
5.If x is NaN, return false.
6.If y is NaN, return false.
7.If x is the same number value as y, return true.
8.If x is +0 and y is -0, return true.
9. If x is -0 and y is +0, return true.
10. Return false.
11.If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions). Otherwise, return false.
12. If Type(x) is Boolean, return true if x and y are both true or both false. Otherwise, return false.
13.Return true if x and y refer to the same object or if they refer to objects joined to each other (see 13.1.2). Otherwise, return false.
14. If x is null and y is undefined, return true.
15. If x is undefined and y is null, return true.
16.If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
17.If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x)== y.
18. If Type(x) is Boolean, return the result of the comparison ToNumber(x)== y.
19. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
20.If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
21.If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x)== y.
22. Return false.
复制代码

乍看上面的算法又长又难记,咱们能够稍微作点简化(参考工具:JavaScript "loose" comparison step by step),简化以下:html

1. If Type(x) is the same as Type(y), then
    1.a. Return the result of performing Strict Equality Comparison x === y.
2. If x is null and y is undefined, return true
3. If x is undefined and y is null, return true
4. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
5. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.
6. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
8. If Type(x) is either String, Number, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
9. If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.
10. Return false.
复制代码

由上面能够清晰的看出,当x和y类型相等或者在类型转换成相同的类型后,宽松相等(==)就能够转换成严格相等(===)的问题。最大的难点是后面的流程。java

当x和y类型不一样时:算法

  • null与undefined比较,返回true
  • Number与String类型比较,利用ToNumber()将String转换成Number再比较
  • 若是有一者为Boolean,利用ToNumber()将Boolean转换成Number再比较
  • 若是有一者为Object类型,利用ToPrimitive()抽象操做函数转换以后再比较

Type(x)和ToPrimitive(x)抽象函数

在阐述Type(x)和ToPrimitive(x)抽象操做函数以前,再介绍一下JavaScript "loose" comparison step by step。咱们能够利用这个工具,清楚的看到x==y的详细过程,每一轮的判断过程以及类型转换的过程,更重要的是,咱们能够点击Type(x)、ToPrimitive(x)等抽象操做函数的详细说明。bash

42 == "42" 的分析过程:42 == "42" ---> 42 == 42 ---> 42 === 42 ---> return true。ecmascript

Type(x)

在单击Type(x)以后,跳转到ECMAScript Language Types,从而可知,Type(x)抽象函数就是得出x的类型,全部类型包括 Undefined, Null, Boolean, String, Symbol, Number, 和 Object。须要强烈注意的是:Type(null) 的类型是Null而不是Object函数

ToPrimitive(x)

ToPrimitive(x)就是一个重点中的难点了。参见ES规范7.1,ToPrimitive( input [ , PreferredType ])是包含两个参数的抽象的操做,一个是 input argument 参数,一个是可选参数 PreferredType,该操做就是将 input argument 转换为 no-Object type (非 object 类型的值,即js数据类型的5种普通类型)。工具

这里咱们只分析PreferredType为空的状况,此时hint为default,进而设置成number。 返回OrdinaryToPrimitive(input, hint)函数。

分析OrdinaryToPrimitive(O, hint)算法,hint为number时,methodNames为 « "valueOf", "toString" »,顺序遍历methodNames中的方法,若是方法存在而且返回值为基本类型,则返回该值。也就是ToPrimitive(x)的值。也就是说,当hint为number时,先执行valueOf()函数,若是返回值为基本类型就再也不调用toString()函数。若是valueOf()和toString()都不存在或者返回值均不为基本类型值,则返回TypeError的错误信息。

示例

var b = {
  valueOf: function () {
    return 3
  },
  toString: function () {
    return 5
  }
}

b == '3'  // true
b == '5'  // false
复制代码

宽松相等的极端状况

[] == ![] // true
复制代码

分析:!运算符会根据ToBoolean规则,进行布尔值的显式强制类型转换(同时反转奇偶校验位)。Boolean([])为true,因此![]为false,即[] == ![] ===> [] == false ===> [] == 0,最后的结果为true了。ui

总结

宽松相等(==)中会有大量的强制类型转换(显式和隐式),在部分状况下确实很危险。但做为一名成熟的开发人员,咱们应该学会有效的运用强制类型转换。spa

参考文献

相关文章
相关标签/搜索