寬鬆相等(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

參考文獻

相關文章
相關標籤/搜索