相等不相等?
先來隨便舉幾個?吧~
'0' == true //?
[1] == [1] //?
[1] == 1 //?
null == false //?
null == undefined //?
NaN === NaN //?
+0 === -0 //?
Object.is([], []) //?
Object.is(-0, +0) //?
Object.is(NaN, NaN) //?
var arr = [NaN, 0, +0]
arr.indexOf(-0) //?
arr.indexOf(NaN) //?
arr.includes(-0) //?
arr.includes(NaN) //?
可能 ±0、NaN 會糾結一點,還是比較基礎的,也許很多人一眼掃過去便知道答案了,網上也已經有了很多相關的經驗總結。我在這裡結合官方規範進行了整理,希望能帶給你不一樣的認識。
預備知識
ECMAScript Language Types
傳送門。根據最新規範,EcmaScript 一共有7種語言型別:
- Undefined
- Null
- Number
- String
- Boolean
- Symbol
- Object
我們經常把 Object 型別稱為 引用資料型別,其它5種則為基本資料型別。(Symbol怎麼說..?
ToNumber
傳送門。任意 EcmaScript 型別轉化為 Number 型別:
型別 | 結果 | |
---|---|---|
Undefined | NaN | |
Null | +0 | |
Boolean | true -> 1,false -> +0 | |
Number | 不轉變 | |
String | 空字串 -> +0,有效的數字 -> 十進位制數字,其它 -> NaN | |
Object | 先ToPrimitive(hint Number),再ToNumber | |
Symbol | 拋錯,TypeError 錯誤 |
ToPrimitive
傳送門。內部方法,主要功能是將引用資料型別轉化為基本資料型別。
- 根據內部標記
hint
的不同有不同的呼叫順序。 -
hint
有三種:default、number、string。default 預設遵照 number 規則。 - default/number:先 valueOf,後 toString。一般轉化規則皆如此。
- string:先 toString,後 valueOf。如Date物件方法、String()轉化等。
- 如果 toString/valueOf 中某一方法返回型別不為物件型別,則直接返回該值,不會繼續呼叫後面方法。如果兩者都返回物件型別,會拋 TypeError 錯誤。
-0、+0、0 的疑惑
明明日常沒什麼卵用,為什麼會有±0?
- 其實遵從
IEEE754
標準的程式語言都有±0的概念,IEEE754
標準的64位浮點數,是以1+11+53
形式的符號位+階數位+尾數位
表示。 - 符號位、階數位、尾數位都是0,那便是
+0
,也就是常規的數字0
。 - 符號位為1,階數位、尾數位都是0,那便是
-0
。 -
IEEE754
還規定了NaN
、無窮
及其它的相應規範,有興趣可自行查詢相關資料。
PS
這部分其實是後加的,你會發現每個知識點都是緊密相連的,構成了一個龐大的知識網路,限於篇幅我不會詳細介紹,但我會盡量貼出規範出處,大家可自行研究。
SameValueNonNumber 內部方法
SameValueNonNumber 方法接收兩個引數 x 和 y ,其中 x 和 y 都不是 Number 型別,該方法返回 true 或 false。
主要規則
- 斷言:x 不是 Number 型別。
- 斷言:x 和 y 是 相同型別。
- 如果 x 是 Undefined 型別,返回 true 。
- 如果 x 是 Null 型別,返回 true 。
-
如果 x 是 String 型別:
- 如果 x 和 y 長度相同且相應編碼單元相同,返回 true 。
- 否則返回 false 。
-
如果 x 是 Boolean 型別:
- 如果 x 和 y 都是true 或者 都是false,返回 true 。
- 否則返回 false 。
-
如果 x 是 Symbol 型別:
- 如果 x 和 y 都是相同 Symbol 值,返回 true 。
- 否則返回 false 。
- 如果 x 和 y 指向同一物件,返回 true 。否則返回 false 。
小結
相同型別比較規則(除Number型別)
- 都是 undefined,相等。
- 都是 null,相等。
- String 型別中,都是相同字串,相等。
- Boolean 型別中,都是 true 或者 都是 false,相等。
- Symbol 型別中,都是相同 Symbol 值,相等。
- Object 型別中,引用同一物件,相等。
使用
哪些 JavaScript 公開方法採用了 SameValueNonNumber 比較呢?
- 公開方法木有
- 接著看下去你就會知道,撇開數值型別比較,
SameValueNonNumber
是SameValue
、SameValueZero
、===
的公共方法。
SameValueZero 內部方法
SameValueZero 方法接收兩個引數 x 和 y ,其中 x 和 y 是 EcmaScript 任意型別值,該方法返回 true 或 false。
主要規則
- 如果 x 和 y 的型別不同,返回 false 。
-
如果 x 是 Number 型別:
- 如果 x 和 y 都是 NaN ,返回 true 。
- 如果 x 是 -0 ,y 是 +0 ,返回 true 。
- 如果 x 是 +0 ,y 是 -0 ,返回 true 。
- 如果 x 和 y 數值相等,返回 true 。
- 返回 false 。
- 返回 SameValueNonNumber(x, y) 方法的返回值。
小結
- 不同型別不相等。
- Number 型別中:±0 相等。NaN 和 NaN 相等。其它相同數值相等。
-
SameValueNonNumber
比較:- 都是 undefined,相等。
- 都是 null,相等。
- String 型別中,都是相同字串,相等。
- Boolean 型別中,都是 true 或者 都是 false,相等。
- Symbol 型別中,都是相同 Symbol 值,相等。
- Object 型別中,引用同一物件,相等。
使用
哪些 JavaScript 公開方法採用了 SameValueZero 比較呢?
- Array.prototype.includes
- Map.prototype.delete
- Map.prototype.has
- Map.prototype.set
- Set.prototype.delete
- Set.prototype.has
- Set.prototype.add
- ArrayBuffer 和 DataView 部分方法
SameValue 內部方法
SameValue 方法接收兩個引數 x 和 y ,其中 x 和 y 是 EcmaScript 中任意型別值,該方法返回 true 或 false。
主要規則
- 如果 x 和 y 的型別不同,返回 false 。
-
如果 x 是 Number 型別:
- 如果 x 和 y 都是 NaN ,返回 true 。
- 如果 x 是 -0 ,y 是 +0 ,返回 false 。
- 如果 x 是 +0 ,y 是 -0 ,返回 false 。
- 如果 x 和 y 數值相等,返回 true 。
- 返回 false 。
- 返回 SameValueNonNumber(x, y) 方法的返回值。
小結
- 不同型別不相等。
- Number 型別中:±0 不相等。NaN 和 NaN 相等。其它相同數值相等。
-
SameValueNonNumber
比較:- 都是 undefined,相等。
- 都是 null,相等。
- String 型別中,都是相同字串,相等。
- Boolean 型別中,都是 true 或者 都是 false,相等。
- Symbol 型別中,都是相同 Symbol 值,相等。
- Object 型別中,引用同一物件,相等。
使用
哪些 JavaScript 公開方法採用了 SameValue 比較呢?
- Object.is
- 在最新的 ES 規範 中,你會發現許多其它內部方法和公開方法都應用了
SameValue
比較方法,其中大部分也沒有涉及數值比較。 - 至於為什麼是
SameValue
方法,而不是SameValueZero
或===
。其實我也不知道。。。我個人傾向於認為:SameValue 方法原本在 ES5 規範中便存在了,最新的規範是為了保持規範一致而繼續沿用。
=== 嚴格相等運算
Strict Equality Comparison,x === y,返回 true 或者 false。
主要規則
- 如果 x 和 y 的型別不同,返回 false 。
-
如果 x 是 Number 型別:
- 如果 x 是 NaN ,返回 false 。
- 如果 y 是 NaN ,返回 false 。
- 如果 x 和 y 數值相等,返回 true 。
- 如果 x 是 -0 ,y 是 +0 ,返回 true 。
- 如果 x 是 +0 ,y 是 -0 ,返回 true 。
- 返回 false 。
- 返回 SameValueNonNumber(x, y) 方法的返回值。
小結
- 不同型別不相等。
- Number 型別中:±0 相等。NaN 和 NaN 不相等。其它相同數值相等。
-
SameValueNonNumber比較:
- 都是 undefined,相等。
- 都是 null,相等。
- String 型別中,都是相同字串,相等。
- Boolean 型別中,都是 true 或者 都是 false,相等。
- Symbol 型別中,都是相同 Symbol 值,相等。
- Object 型別中,引用同一物件,相等。
使用
哪些 JavaScript 公開方法採用了 === 比較呢?
- === 嚴格相等運算
- 左右兩邊是相同型別的 == 相等運算
- switch語句中的case
- Array.prototype.indexOf
- Array.prototype.lastIndexOf
== 相等運算
Abstract Equality Comparison,x == y,返回 true 或者 false。
主要規則
-
如果 x 和 y 的型別相同:
- 返回嚴格相等運算結果 x === y 。
- 如果 x 是 null ,y 是 undefined ,返回 true 。
- 如果 x 是 undefined ,y 是 null ,返回 true 。
- 如果 x 是 Number 型別 ,y 是 String 型別,返回 x == ToNumber(y) 運算結果。
- 如果 x 是 String 型別 ,y 是 Number 型別,返回 ToNumber(x) == y 運算結果。
- 如果 x 是 Boolean 型別 ,返回 ToNumber(x) == y 運算結果。
- 如果 y 是 Boolean 型別 ,返回 x == ToNumber(y) 運算結果。
- 如果 x 是 Number、String、Symbol 中任意一個型別 ,y 是 Object 型別,返回 x == ToPrimitive(y) 運算結果。
- 如果 y 是 Number、String、Symbol 中任意一個型別 ,x 是 Object 型別,返回 ToPrimitive(x) == y 運算結果。
- 返回 false 。
小結
- 相同型別:遵循 === 嚴格相等比較規則。
- null == undefined,相等。
-
不同型別:
- 基本資料型別轉換為 Number 型別再 == 比較。
- 引用資料型別執行內部 ToPrimitive方法後再 == 比較。
使用
哪些 JavaScript 公開方法採用了 == 比較呢?
- 只有這隻 == 相等運算
相等不相等
開頭的答案。如果對結果感到好奇,不妨對著上面的過程比對~
'0' == true // false
[1] == [1] // false
[1] == 1 // true
null == false // false
null == undefined // true
NaN === NaN // false
+0 === -0 // true
Object.is([], []) // false
Object.is(-0, +0) // false。見SameValue
Object.is(NaN, NaN) // true。見SameValue
var arr = [NaN, 0, +0]
arr.indexOf(-0) // 1。見===
arr.indexOf(NaN) // -1。見===
arr.includes(-0) // true。見SameValueZero
arr.includes(NaN) // true。見SameValueZero
總結
-
SameValueZero
、SameValue
、===
這仨完全差不多嘛!-
相同點:
- 不同型別即不相等。 - 相同型別遵從`SameValueNonNumber`規則。
- 不同點:對
±0
、NaN
的判斷上各有不同。
-
-
以 Array.prototype.includes 為代表的
SameValueZero
- ±0 相等
- NaN 和 NaN 相等
-
以 Object.is 為代表的
SameValue
- ±0 不相等
- NaN 和 NaN 相等
-
以 ===、Array.prototype.indexOf 為代表的
===
- ±0 相等
- NaN 和 NaN 不相等
-
==
- 相同型別採用
===
嚴格比較。 -
不同型別會隱式轉換:
- 基本資料型別轉換為 Number 型別再 == 比較。
- 引用資料型別執行 ToPrimitive 轉換後再 == 比較。
- undefined/null 特例。
- 相同型別採用