先看一道坑爹的題目:
if([]) console.log(1);
if({}) console.log(2);
if([] == false) console.log(3);
if({} == false) console.log(4);
if([1] == [1]) console.log(5);
問:最終的結果是什么?
眾所周知,js中==與===是有很大區別的,主要在于==會先嘗試將兩邊的變量轉化為同一類型再進行比較,這就導致使用==的時候,即使兩邊的東西從外觀上看風馬牛不相及,最后也可能得到true的結果,以上的題目便是幾個例子。
但實際上,js相等運算符的比較結果還是有跡可循的,參考了網上的資料,總結如下:
抽象相等
考慮 x == y 的結果時
一、 Type(x)與Type(y)相同:
- Type(x)為undefined或null,結果為true
- Type(x)為Number,則比較數字的值,有幾個特殊情況:當x、y至少一個為NaN時,結果為false;-0 == +0結果為true
- Type(x)為String,與常識一致
- Type(x)為Boolean,與常識一致
- Type(x)為Object,必須相同引用才會得到true,即[1] == [1]結果為false
二、 Type(x)與Type(y)不同:
- undefined == null,結果為true
- String與Number比較,將String轉換為Number后比較,例:1 == "1",結果為true
- Boolean與Number比較,將Boolean轉換為Number后比較,例:true == 1,false == 0,結果均為true
Object與String或Number比較,將Object轉換為String,再使用對應的規則繼續進行轉換,例:["1"] == 1,先將["1"]轉換為"1",再將"1"轉換為1,最后結果為true-
其他結果為false
(這部分的更正在下方!)
嚴格相等
考慮 x === y 的結果時
一、 Type(x)與Type(y)不同:結果為false,這回干脆多了
二、 Type(x)與Type(y)相同:
- Type(x)為undefined或null,結果為true
- Type(x)為Number,則比較數字的值,有幾個特殊情況:當x、y至少一個為NaN時,結果為false;-0 == +0結果為true
- Type(x)為String,與常識一致
- Type(x)為Boolean,與常識一致
- Type(x)為Object,必須相同引用才會得到true,即[1] == [1]結果為false
總結
當Type(x)與Type(y)相同時,抽象相等與嚴格相等是沒有什么區別的;當Type(x)與Type(y)不同時,嚴格相等的結果一律為false,但是抽象相等會有很多種情況,也是最容易出錯的地方。
經過再次的學習,我認識到關于抽象相等 ,其實是可以拋棄之前繁瑣的規則的,并且之前的說法有些謬誤,技巧如下圖:
一張圖總結 == 運算符
- 首先,undefined 與 null 互相相等,但與其他類型均不等(就是圖右半部分)
- Boolean 與 String 之間比較,先轉化成Number,再進行 === 比較
- Boolean/String與Number比較,也是先轉化成Number,再進行 === 比較(以上兩條為圖左半部分的下半部分,N意為Number強制轉換)
- Object與Boolean、String、Number類型比較,需要先進行toPremitive(obj),得到的結果,再進行 == 比較。
ToPrimitive(A)通過嘗試依次調用 A 的A.toString() 和 A.valueOf() 方法,將參數 A 轉換為原始值(Primitive)。
上面是MDN關于ToPrimitive的原文,但是太簡略了,實際情況比這個要復雜一點
經過我自己的測試,首先 ToPrimitive(A) 是會先調用 A.valueOf() 的,但是只有在 A.valueOf() 得到的是原始值時,才會使用;如果 A.valueOf() 結果仍然是一個對象,就會再去調用 A.toString()
重點就是第四條!我們帶著第四條來解析題目!
if([] == false) console.log(3);
// 調用toPremitive([]),先看 [].valueOf() 的結果,發現結果仍然是 [],不是原始值!
// 繼續調用[].toString(),這時結果是 "",終于是原始值了
// 然后比較 false == "",此時應用第三條,兩邊都轉化成0,結果是 true
if({} == false) console.log(4);
// 和上一個類似,調用toPremitive({}),先看 {}.valueOf() 的結果,發現結果仍然是 {},不是原始值!
// 繼續調用{}.toString(),這時結果是 "[object Object]",終于是原始值了
// 然后比較 false == "[object Object]",應用第三條,左邊轉化成0,右邊轉化成NaN,結果是 false
其他題目解析
if([]) console.log(1); // 1,引用類型的內存地址不為空,所以都是true
if({}) console.log(2); // 2,引用類型的內存地址不為空,所以都是true
if([1] == [1]) console.log(5); // 無打印,兩個Object不是指向同一個引用,結果為false