從[] == ![] 看隱式強制轉換機制

Annn發表於2017-08-19

寫在最前

本次分享一下通過ES5規範來總結如何準確的計算“==”的執行結果。由於規範是枯燥無味的,所以作者試圖總結了其中的規律,並希望可以讓讀完這篇文章的讀者不再去“死記硬背”==的結果是什麼,而是通過幾次簡單的計算便心有成竹的得出結論!

歡迎關注我的部落格,不定期更新中——

JavaScript小眾系列開始更新啦

——何時完結不確定,寫多少看我會多少!這是已經更新的地址:

這個系列旨在對一些人們不常用遇到的知識點,以及可能常用到但不曾深入瞭解的部分做一個重新梳理,雖然可能有些部分看起來沒有什麼用,因為平時開發真的用不到!但個人認為糟粕也好精華也罷裡面全部蘊藏著JS一些偏本質的東西或者說底層規範,如果能適當避開舒適區來看這些小細節,也許對自己也會有些幫助~文章更新在我的部落格,歡迎不定期關注。

先看實驗程式碼

2 == true               //false
2 == false              //false
[] == false             //true
"0" == false            //true
[] == ![]               //true 神奇吧複製程式碼

我相信大部分的童鞋看著這種等式一般的反應都是xxx是真值,可以轉換為true。xxx是假的所以是false!好的摒棄這種想法吧,不然也不會出現這麼多神奇的結果了,我們需要做的是通過一步步計算來得出結論。

前置知識

這部分知識屬於真·死記硬背,因為你問我為什麼,我只能說規範就是這麼定義的。

假值

為什麼提到假值,而不是真值是因為真值真的是太!多!了!但是假值只有以下這麼幾個:

  • undefined
  • null
  • false
  • +0、-0、NaN
  • ""

除此以外別的值做強制型別轉換的時候都是真值,so記住就好。
PS:有興趣的同學可以試試new Number(0)之類的通過物件包裝的假值的結果,不過這並不常用故不屬於本次討論範疇。

!

! 這個運算子,會進行顯式強制轉化,將結果轉化為布林值即true或false。例如:

![] //false
!1  //false
!0  //true複製程式碼

以此類推來進行顯式的強制轉換

undefined == null

參考規範11.9.3節抽象相等比較演算法可得出
undefined == null 為true的結論。
PS:本次計算規則為抽象相等比較演算法的總結,細節可參考上文11.9.3節規範。

ToPrimitive


這是規範9.1節的內容,簡單來說你只需要知道如果某個物件([], {})之類的要進行隱式型別轉換,那麼裡面會順序執行兩個操作。即x.valueOf().toString()。這裡有一個不常用的點要注意。我說的是物件型別進行“隱式”型別轉化,如果是顯式則不是如此。看下例子:

var a = {
    valueOf: () => 1,
    toString: () => 233
}
a + ""     // 1
String(a)  // 233複製程式碼

隱式轉化是按照先valueOf後toString的順序執行,如果顯式呼叫會直接執行oString,不過顯式呼叫在js中覆蓋率沒有隱式的多,知道即可。

計算 x == y 規則

x,y如果型別相同

這個部分相信有問題的同學百度一下就好。數字的比大小,字串比大小。裡面需要小心的就是NaN != NaN 以及 物件如何比較大小?([1] != [1])

重點:x,y型別不同

x,y一方為布林值

如果x,y其中一個是布林值,那麼對這個布林值進行toNumber操作。發現問題了麼童鞋們,來看下面程式碼:

42 == true   // false複製程式碼

不瞭解規範的會認為,42是真值啊!42會轉換為true!你別說如if(42){}這個42確實是真值。但是我們現在在討論“==”下的轉換,那麼請記住規範規定了:型別不同時若一方是布林值,是對布林值進行型別轉化即true => 1,之後我們就可以理解為什麼42不等於true了因為1!= 42

x,y為數字和字串

將字串的一方進行toNumber()操作,這個不難理解哈

x,y一方為物件

將物件進行ToPrimitive()操作。如何操作見上文。

計算示例程式碼結果

2 == true

true => 一方為布林值:true => 1
2 != 1複製程式碼

2 == false

true => 一方為布林值:false => 0
2 != 0複製程式碼

[] == false

1、[]為物件: ToPrimitive([]) => [].valueOf().toString() => ""
2、false為布林:false => 0
3、等式變為:"" == 0
4、一方為數字,一方為字元
    Number("") => 0
    => 0 == 0複製程式碼

"0" == false

1、false為布林:false => 0
2、等式變為:"0" == 0
3、一方為數字,一方為字元
    Number("0") => 0
    => 0 == 0複製程式碼

終極版 [] == ![]

1、左側[]為物件: ToPrimitive([]) => [].valueOf().toString() => ""
2、右側![]先進行顯式型別轉換:false(除了上文提到的假值剩下都是真值)
3、等式變為: "" == false
4、一方為布林:false => 0
5、等式變為:"" == 0
5、一方為數字,一方為字元
    Number("") => 0
    => 0 == 0複製程式碼

所以你會發現這些看起來神奇的效果,不過是一步步按照規則進行強制轉換罷了。希望以後大家再遇到這種神奇等式的時候不要靠記憶誰是誰,而是一步步推算你會發現結果也不過如此,扮豬吃老虎罷了~

參考文獻

  1. ES5規範
  2. 《你不知道的JavaScript(中卷)》

最後

慣例po作者的部落格,不定時更新中——
有問題歡迎在issues下交流,捂臉求star=。=

相關文章