JavaScript 核心觀念(17)-運算子、型別與文法-寬鬆相等、嚴格相等以及隱含轉型
前言
JavaScript 嚴格說起來其實真的是一個非常雞婆的語言,因為它會很雞婆幫你做轉換型別。
寬鬆相等、嚴格相等以及隱含轉型
在一開始我們先來看一段程式碼
1 | 1 == '1'; // true |
當你輸入上方判斷時,會得到一個 true
的答案,看起來是沒有什麼問題,但若改成三個等號結果就不一樣了
1 | 1 === '1'; // false |
在這邊三個等號稱之為「嚴格相等」反之兩個等號則是「寬鬆相等」,所謂的寬鬆相等是什麼意思呢?
當你做寬鬆相等時,JavaScript 會因為兩者型別的不同而做所謂的「隱含轉型」,簡單來講就是將兩邊轉換成相同類型的型別,例如上面的題目
1 | 1 == '1'; // true |
其中字串 1
就被轉換成 Number
,在這邊我們可以透過 MDN 的文件來了解其規則
以上面範例來講,比較 A 遇到比較 B 可以看到 A === ToNumber(B)
因此我們可以知道字串 '1'
會被轉換成 Number。
那該如何驗證呢?課堂上使用了十六進位來驗證其對照表是否正確,例如 0x11
若遇到 Number()
是會被轉換成數字 17
1 | 17 == 0x11; // true |
那假使若是 Boolean 比較字串呢?
1 | false == '0'; // true |
在 MDN 對照表上來看,若是布林值遇到字串是會兩者都轉換成數字 ToNumber(A) === ToNumber(B)
,因此若是這樣打的話
1 | true == 'true'; // false |
你會發現結果是一個 false
其主要原因是 Number('true')
是會出現一個 NaN(Not a Number) 的錯誤。
接下來講講 Object 與 Array 是如何做寬鬆相等的
1 | 10 == [10]; // true |
這邊首先要了解一件事情 Array 也是物件的一種,因此在做寬鬆比對時,會被包裹物件轉換,其中依照 MDN 說明就是 A === ToNumber(B)
1 | Number([10]); // 數字 10 |
基本上這邊的重點就是「物件與非物件比對時是使用包裹物件來轉換」,其中嚴格相等為什麼沒有特別去說明是因為「嚴格相等不會有隱含轉型的問題,因為在嚴格相等時是以當前型別去做比對」,只需要知道這一句話就可以了,舉例來講上面的範例
1 | 1 === '1'; // false |
因為不會被隱含轉型,因此就會是 Number 比較 String。
最後擷取一下關於 MDN 提供的轉型表中的一些重點文字
根據上表, ToNumber(A) 嘗試在比較前轉換成一個數字。
ToPrimitive(A) 嘗試從物件轉換成原生值,透過嘗試對 A 使用 A.toString 和 A.valueOf 方法。
舉例來講當第一個比較值是物件,第二個若是布林值,則依照規格表來看會是 ToPrimitive(A) == ToNumber(B)
1 | [0] == false; // false |
在這邊 [0]
轉型時會先調用 toString
看能不能夠執行,在依照 toString
來判斷結果,如果是原始型別,那麼就會直接返回結果,反之若不行則會在調用判斷 valueOf
,若 valueOf
無法回傳原始型別就會直接噴錯
1 | [0].toString(); // 字串 0,是原始型別因此回傳並轉換成 Number(0); |
在這邊我們試著做一個小題目
1 | var a = {}; |
接下來我們可以試著依照上述觀念去查找 ToPrimitive(a)
1 | [0].toString(); // 回傳字串 [object Object] |
因此這邊加號運算子若是遇到數字加上字串,就會變成字串相加 [object Object]1