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
2
[0].toString(); // 字串 0,是原始型別因此回傳並轉換成 Number(0);
[0].valueOf(); // 回傳的是陣列 0。

在這邊我們試著做一個小題目

1
2
var a = {};
a + 1; // ?

接下來我們可以試著依照上述觀念去查找 ToPrimitive(a)

1
[0].toString(); // 回傳字串 [object Object]

因此這邊加號運算子若是遇到數字加上字串,就會變成字串相加 [object Object]1

ToPrimitive

參考文獻