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

寫在最前

本次分享一下通過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

image
image

這是規範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=。=

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章