我對 JS 中相等和全等操做符轉化過程一直很迷惑,直到有了這份算法

做者:Dmitri Pavlutinjavascript

譯者:前端小智html

來源:dmitripavlutin前端


阿里雲最近在作活動,低至2折,有興趣能夠看看: promotion.aliyun.com/ntms/yunpar…java


在平常的 JS 編碼過程當中,可能很難看到相等運算符(==)是如何工做的。特別是當操做數具備不一樣類型時。這有時會在條件語句中產生一些難以識別的 bug。很容易理解爲何 0 == 8flase 的或者 '' == falsetrue。可是爲何{} == truefalse 的就看不出來了。接下將會講這是腫麼肥事。git

在這以前,先說幾個術語:github

  • 操做符(Operator) 表示操做的符號。例如,相等運算符==比較兩個值,三等運算符 === 比較兩個值及其類型,加法運算符+兩個數字和或鏈接兩個字符串。正則表達式

  • 操做數(Operand) 是運算的主體,是執行運算的數量。例如,在表達式 0 == {} 中,0 是第一個操做數,{} 是第二個操做數。算法

  • JS 中的基本數據類型(原始類型)有 numberstring, booleannullundefinedsymbol數組

全等運算符 ===

全等和不全等操做符遵循如下基本規則(IEA規則):瀏覽器

  1. 若是兩個操做數有不一樣的類型,它們不是嚴格相等的
  2. 若是兩個操做數都爲 null,則它們是嚴格相等的
  3. 若是兩個操做數都爲 undefined,它們是嚴格相等的
  4. 若是一個或兩個操做數都是 NaN,它們就不是嚴格相等的
  5. 若是兩個操做數都爲 true 或都爲 false,它們是嚴格相等的
  6. 若是兩個操做數都是 number 類型而且具備相同的值,則它們是嚴格相等的
  7. 若是兩個操做數都是 string 類型而且具備相同的值,則它們是嚴格相等的
  8. 若是兩個操做數都引用相同的對象或函數,則它們是嚴格相等的
  9. 以上全部其餘狀況下操做數都不是嚴格相等的。

規則很簡單。

值得一提的是,在全等運算中,NaN 與其餘任何值相比,結果都是 false。 來看看考慮些例子,這是學習這些規則的好方式。

例 1

1 === "1" // false, 規則 1
複製代碼

操做數是不一樣的類型(數字和字符串),基於 IEA 規則1,它們是不等的。

例 2

0 === 0 // true, 規則 6
複製代碼

操做數具備相同的類型和相同的值,所以根據IEA規則6,它們是嚴格相等的。

例 3

undefined === undefined // true, 規則 3
複製代碼

兩個操做數都是 undefined 的,應用 IEA 規則3,它們是相等的。

例 4

undefined === null // false, 規則 1
複製代碼

由於操做數是不一樣的類型,根據IEA規則1,它們並不相同。

例 5

NaN === NaN // false, IEA 規則 5
複製代碼

操做數是相同的類型,可是IEA 規則4 代表任何與 NaN 比較都是不相等的。

例 6

var firstObject = {},
  secondObject = firstObject;
secondObject['name'] = 'Neo';
secondObject === firstObject // true, IEA 規則 8
複製代碼

兩個變量 firstObjectsecondObject 都是對同一對象的引用,根據 IEA 規則8,它們相等。

例 7

[] === [] //false, IEA 規則 9
複製代碼

字面量 [] 建立了一個新的數組引用。這兩個操做數是相同的類型(對象),可是它們引用不一樣的對象。根據 IEA 規則 9 ,它們不相等。

對象轉換爲原始值的規則

對象到布爾值

對象到布爾值的轉換很是簡單:全部的對象(包括數字和函數)都轉換爲 true。對於包裝對象亦是如此:new Boolean(false) 是一個對象而不是原始值,它將轉換爲 true

對象到字符串

對象到字符串對象到數字 的轉換都是經過調用待轉換對象的一個方法來完成的。一個麻煩的事實是,JS 對象有兩個不一樣的方法來執行轉換,接下來要討論的一些特殊場景更加複雜。值得注意的是,這裏提到的字符串和對象的轉換規則只適用於原生對象(native object)。宿主對象(例若有Web瀏覽器定義的對象)根據各自的算法能夠轉換成字符串和數字。

全部的對象繼承了兩個轉換方法。第一個是toString(),它的做用是返回一個反映這個對象的字符串。默認的 toString() 方法並不會返回一個有趣的值:

({x:1,y:2}).toString()  //=>"[object object]"
複製代碼

不少類定義了更多特定版本的toString()方法。例如,數組的 toString() 方法是將每一個數組元素轉換爲一個字符串,並在元素之間添加逗號後合併成結果字符串。

函數的 toString() 方法返回了這個函數的實現定義。實際上,這裏的實現是一般是將用戶定義的函數轉換爲 JS 源代碼字符串。

日期 DatetoString() 方法返回了一個可讀的日期和時間字符串。

RegExptoString() 方法將RegExp對象轉換爲表示正則表達式直接量的字符串:

來幾個例子:

[1,2,3].toString() //=> "1,2,3"
(function(x){ f(x); }).toString() // => "function(x){ f(x); }"
/\d+/g.toString()   // => "/\d+/g"
new Date(2019,9,16).toString()  //=> "Wed Oct 16 2019 00:00:00 GMT+0800 (中國標準時間)"
複製代碼

另外一個轉換對象的函數是 valueOf()。若是存在任意原始值,它就默認將對象轉換爲表示它的原始值。對象是複合值,並且大多數對象沒法真正表示爲一個原始值,所以默認的 valueOf() 方法簡單地返回對象自己,而不是返回一個原始值。數組、函數和正則表達式簡單地繼承了這個方法,調用這些類型的實例的valueOf() 方法只是簡單返回對象自己。日期 DatevalueOf() 方法會返回它的一個內部表示:1970年1月1日以來的毫秒數。

new Date(2019,9,16).valueOf() // 1571155200000
複製代碼

經過使用 toString()valueOf() 方法,就能夠作到對象到字符串和對象到數字的轉換了。但須要注意的是,在某些特殊的場景中,JS 執行了徹底不一樣的對象到原始值的轉換。

JS 中對象到字符串的轉換通過以下這些步驟,我們簡稱 OPCA 算法。

  1. 若是方法 valueOf() 存在,則調用它。若是 valueOf() 返回一個原始值,JS 將這個值轉換爲字符串(若是自己不是字符串的話),並返回這個字符串結果。

  2. 若是方法 toString() 存在,則調用它。若是 toString() 返回一個原始值,JS 將這個值轉換爲字符串(若是自己不是字符串的話),並返回這個字符串結果。須要注意,原始值到字符串的轉換。

  3. 不然,JS 沒法從 toString()valueOf() 得到一個原始值,它將拋出一個 TypeError:不能將對象轉換爲原始值 異常

當調用 valueOf() 方法時,大多數原生對象都會返回對象自己。所以 toString() 方法使用得更頻繁。

關於 Date 對象的注意事項:在轉換爲原始值時,對象當即使用 toString() 方法轉換爲字符串。這樣,規則1就被跳過了。普通的 JS 對象,{}new object(),一般被轉換成 "[object Object]"

數組經過將它的元素與「,」分隔符鏈接轉換爲。例如 [1,3,"four"] 被轉換成" 1,3,four"

相等運算符 ==

相等運算符 「==」 若是兩個操做數不是同一類型,那麼相等運算符會嘗試一些類型轉換,而後進行比較。

相等運算符算法(EEA)

  1. 若是操做數具備相同的類型,請使用上面的 IEA 測試它們是否嚴格相等。 若是它們不嚴格相等,則它們不相等,不然相等。
  2. 若是操做數有不一樣的類型:
    2.1) 若是一個操做數爲 null 而另外一個 undefined,則它們相等
    2.2) 若是一個值是數字,另外一個是字符串,先將字符串轉換爲數字,而後使用轉換後的值比較
    2.3) 若是一個操做數是布爾值,則將 true 轉換爲 1,將 false 轉換爲 0,而後使用轉換後的值比較
    2.4) 若是一個操做數是一個對象,而另外一個操做數是一個數字或字符串,則使用OPCA將該對象轉換爲原原始值,再使用轉換後的值比較
  3. 在以上的其餘狀況下,操做數都不相等

例 1

1 == true // true
複製代碼

上面的轉換步驟:

  1. 1 == true (使用EEA 規則2.3 將 true 轉換爲 1)
  2. 1 == 1(操做數有相同的類型。使用 EEA 規則1 將相等轉換爲全等運算進行比較
  3. 1 === 1(兩個操做數都是數字,而且具備相同的值。根據 IEA 規則 6,這是相等的)
  4. true

例 2

'' == 0 // true
複製代碼

上面的轉換步驟:

  1. '' == 0(一個操做數是字符串,另外一個操做數是數字,根據EEA規則2.2'' 被轉換爲數字 0 )
  2. 0 == 0(操做數類型相同,使用 EEA規則1 將相等轉換爲全等運算進行比較)
  3. 0 === 0(操做數類型相同,值相同,因此根據IEA規則6,它是一個恆等式)
  4. true

例 3

null == 0 // false
複製代碼

上面的轉換步驟:

  1. null == 0 (null 是原始類型,0 是 number 類型。根據EEA規則3)
  2. false

例 4

null == undefined // true
複製代碼

上面的轉換步驟:

  1. null == undefined(基於EEA規則2.1,操做數相等)
  2. true

例 5

NaN == NaN // false
複製代碼

上面的轉換步驟:

  1. NaN == NaN(兩個操做數都是數字。根據EEA規則1,將相等轉換爲全等運算進行比較)
  2. NaN === NaN(根據IEA規則4,操做數嚴格不相等)
  3. false

例 6

[''] == '' // true
複製代碼

上面的轉換步驟:

  1. [''] == ''(['']是一個數組和 '' 是一個字符串。應用EEA規則2.4並使用OPCA規則2將數組轉換爲原始值 '')
  2. '' == '' (兩個操做數都是字符串,將相等轉換爲全等運算進行比較)
  3. '' === '' (兩個操做數類型相同,值相同。使用IEA規則7,它們是相等的)
  4. true

例 7

{} == true // false
複製代碼

上面的轉換步驟:

  1. {} == true(使用EEA規則2.3,將 true 操做數轉換爲 1)
  2. {} == 1(第一個操做數是一個對象,所以有必要使用OPCA將其轉換爲原始值)
  3. 「[object object]」== 1(由於第一個操做數是字符串,第二個操做數是數字,根據 EEA規則2.2「[object object]」轉換爲數字)
  4. NaN == 1(兩個操做數都是數字,所以使用 EEA規則1 將相等轉換爲全等運算進行比較)
  5. NaN === 1(根據 IEA規則4,沒有什麼是與 NaN 相等的,結果是 false)
  6. false

實用技巧

即便在詳細研究了本文中的全部示例、學習了算法以後,你會發現要當即理解複雜的比較還須要時間的積累。

告訴你一些技巧。 將本文添加到書籤中(使用Ctrl + D),下一次看到有趣的狀況時,能夠根據等式算法編寫逐步的計算。 若是檢查至少 10 個示例,則之後不會有任何問題。

如今就能夠試試,如 [0] == 0 的結果和轉化步驟是什麼?

相等運算符==進行類型轉換。所以,可能會產生意想不到的結果,例如 {}== truefalse( 參見例7)。在大多數狀況下,使用全等操做符 === 更安全。

總結

相等和全等運算符號多是最經常使用的運算符之一。理解它們是編寫穩定且bug較少的 JS 的步驟之一。

代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug

原文:dmitripavlutin.com/the-legend-…

交流(歡迎加入羣,羣工做日都會發紅包,互動討論技術)

阿里雲最近在作活動,低至2折,有興趣能夠看看:promotion.aliyun.com/ntms/yunpar…

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

github.com/qq449245884…

由於篇幅的限制,今天的分享只到這裏。若是你們想了解更多的內容的話,能夠去掃一掃每篇文章最下面的二維碼,而後關注我們的微信公衆號,瞭解更多的資訊和有價值的內容。

clipboard.png

每次整理文章,通常都到2點才睡覺,一週4次左右,挺苦的,還望支持,給點鼓勵

相關文章
相關標籤/搜索