此篇會依照 ECMA-262 11th(ES2020)規則,來深入探討比較運算子轉型的規則。
比較運算子注意事項
在 JavaScript 中除了基礎型別(Primitive Type)以外的型別(ex: 陣列、物件)在做比較時,判斷的是兩者是否指向同一個 reference
,而非判斷兩者的值是否相同。
e.g. 陣列判斷
1 | let a = [1 , 2]; |
下方會在針對其它例外情況做詳細介紹。
==
equality:比較等號兩邊值是否相同,當等號兩邊型別不同時,會先依照規則轉型再進行比對。(除了物件外,幾乎會轉型為 number 後在做比較)
e.g. 基礎 equality
1 | console.log('1' == 1); // true |
下方會解析 ECMA-262 11th(ES2020)規範:7.2.15 Abstract Equality Comparison 是如何比較的以及轉型的過程。
Step1:先檢查型別,型別相同則回傳 x === y 的結果。
當型別相同,則不需要轉型因此可以直接比較。
1 | console.log('b' === 'a'); // false |
Step2:等號兩邊為 null 和 undefined 回傳 true。
1 | console.log(undefined == null); // true |
Step3:基礎型別自動轉成 number 後在做比較。(透過原生函式 ToNumber)
1 | console.log('1' == 1); // true,1 == 1 |
⭐️ Step4:先將非基礎型別轉成基礎型別後再做比較。(透過原生函示 toPrimitive)
toPrimitive 將物件透過原生方法(ex:valueOf、toString)轉成基礎型別。
string 的執行順序
- 先執行 toString > 還不是 primitive 在執行 valueOf > 還不是 primitive 就噴
TypeError
。
number 的執行順序
- (PreferredType 預設為 number)先執行 valueOf > 還不是 primitive 在執行 toString > 還不是 primitive 就噴
TypeError
。
e.g. 觀察 toPrimitive 中 valueOf 、 toString 優先順序
1 | let obj = { |
e.g. toPrimitive 自定義方法,依照 hint 種類輸出結果。
1 | let obj = { |
以上為 == 比較運算子的自動轉型過程。
===
strict equality:比較等號兩邊 型別
以及 數值
是否 都相同
。
e.g. 基礎 strict equality
1 | console.log('1' === 1); // false |
下方會解析 ECMA-262 11th(ES2020)規範:7.2.16 Strict Equality Comparison 是如何比較的以及轉型的過程。
Step1:檢查型別,型別不同則回傳 false。
1 | console.log('1' === 1); // false |
Step2:型別為 number 時會回傳 Number::equal ( x, y ) 結果,型別為 BigInt 時會回傳 BigInt::equal ( x, y ) 結果。
在這之前需要,先暸解 Number、BigInt 兩者的 equal ( x, y ) 規則。
Number::equal ( x, y ) 規則:
- 有 NaN 就回傳 false。
- 值相同就回傳 true。
- 兩者為 0 就回傳 true。(不管正負)
1 | console.log(NaN === NaN); // false |
BigInt::equal ( x, y ) 規則:
- BigInt 值相同回傳 true,否則回傳 false。
根據 MDN 文件:「BigInt 是一個內建的物件,提供了表示大於 “2 的 53 次方” 整數的功能 (“2 的 53 次方” 是 JavaScript 原生的 Number 能夠表示的最大值)」
1 | console.log(1n === 1n); // true |
Step3:會依照 SameValueNonNumeric 規則回傳結果。
SameValueNonNumeric ECMA Assert:
- 兩者不是
Number
或BigInt
型別。 - 兩者值不相同。
SameValueNonNumeric 規則:
- 有
undefined
回傳 true。 - 有
null
回傳 true。 字串型別
時判斷字串是否相同,相同回傳 true 否則回傳 false。boolean
、symbol
和object
型別都是比較值是否相同 。(但 object、symbols 還需要比較 reference 是否相同)
1 | console.log(true === true); // true |
conclusion
object
、symbols
還需要比較 reference,指向同一個references
才會回傳 true。===
不會做自動轉型。==
除了物件外,會轉型為 number 在做比較。- 判斷是否為
null
、undefined
可以使用==
替代===
。
1 | if (x === null || x === undefined) { |
常見迷思
== 不會檢查型別,直接轉型。
== 第一步不是直接轉型,而是先檢查型別,再依造規則做轉型。
分享幾張 == 、 ===、if 的視覺化圖表
最後一張:Relational and Equality Operators
最後建議新手一律使用嚴謹的 === 進行比較,避免因為自動轉型而產生非預期的結果,有經驗的就可以自由使用兩者囉。
references
白話文:前端三十|13. [JS] 為什麼判斷相等時不能用雙等號?
簡單解析ES6文件:JavaScript 可愛筆記 #0 – 兩個等於真的有那麼壞嗎?
解析 ToPrimitive:js隐式装箱-ToPrimitive
stackoverflow 討論:Which equals operator (== vs ===) should be used in JavaScript comparisons?