close

某次面試,面試官突然問道:「如何讓 x 等於 1 且讓 x 等於 2 且讓 x 等於 3 的等式成立?」

話音剛落,筆者立馬失去意識,雙眼一黑,兩腿一蹬,心裡暗罵:什麼玩意兒!

雖然當時沒回答上來,但覺得這題非常有意思,便在這為大家分享下後續的解題思路:

寬鬆相等 == 和嚴格相等 === 都能用來判斷兩個值是否「相等」,首先,我們要明確上文提到的等於指的是哪一種,我們先看下二者的區別:

(1) 對於基礎類型之間的比較,== 和 === 是有區別的:

不同類型間比較,== 比較「轉化成同一類型後的值」看「值」是否相等,=== 如果類型不同,其結果就是不等

同類型比較,直接進行「值」比較,兩者結果一樣。

(2) 對於引用類型之間的比較,== 和 === 是沒有區別的,都進行「指針地址」比較。

(3) 基礎類型與引用類型之間的比較,== 和 ===是有區別的:

因為類型不同,=== 結果為 false

對於 ==,將引用類型轉化為基礎類型,進行「值」比較。

「== 允許在相等比較中進行強制類型轉換,而 === 不允許。」

由此可見,上文提到的等於指的寬鬆相等 ==,題目變為 「x == 1 && x == 2 && x == 3」。

那多種數據類型之間的相等比較又有哪些呢?筆者查閱了相關資料,如下所示:

同類型數據之間的相等比較

如果 Type(x) 等於 Type(y) ES5 規範 11.9.3.1 (https://262.ecma-international.org/5.1/#sec-11.9.3) 這樣定義:

如果 Type(x) 是 Undefined,返回 true。
如果 Type(x) 是 Null,返回 true。
如果 Type(x) 是 Number ,則
如果 x 是 NaN,返回 false。
如果 y 是 NaN,返回 false。
如果 x 與 y 的數字值相同,返回 true。
如果 x 為 +0,y 為 -0,返回 true。
如果 x 為 -0,y 為 +0,返回 true。
如果 Type(x) 是 String,則如果 x 和 y 是字符的序列完全相同(相同的長度和相同位置相同的字符),則返回 true。否則,返回 false。
如果 Type(x) 是 Boolean,則如果 x 和 y 都為 true 或都為false,則返回 true。否則,返回 false。
如果 x 和 y 指向同一對象,則返回 true。否則,返回 false。
null 和 undefined 之間的相等比較

null 和 undefined 之間的 == 也涉及隱式強制類型轉換。ES5 規範 11.9.3.2-3 這樣定義:

如果 x 為 null,y 為 undefined,則結果為 true。
如果 x 為 undefined,y 為 null,則結果為 true。

在 == 中,null 和 undefined 相等(它們也與其自身相等),除此之外其他值都不和它們兩個相等。

這也就是說, 在 == 中null 和 undefined 是一回事。

vara=null;varb;a==b;//truea==null;//trueb==null;//truea==false;//falseb==false;//falsea=="";//falseb=="";//falsea==0;//falseb==0;//false字符串和數字之間的相等比較

ES5 規範 11.9.3.4-5 這樣定義:

如果 Type(x) 是數字,Type(y) 是字符串,則返回 x == ToNumber(y) 的結果。
如果 Type(x) 是字符串,Type(y) 是數字,則返回 ToNumber(x) == y 的結果。
vara=42;varb="42";a===b;//falsea==b;//true

因為沒有強制類型轉換,所以 a === b 為 false,42 和 "42" 不相等。

根據規範,"42" 應該被強制類型轉換為數字以便進行相等比較。

其他類型和布爾類型之間的相等比較

ES5 規範 11.9.3.6-7 這樣定義:

如果 Type(x) 是布爾類型,則返回 ToNumber(x) == y 的結果。
如果 Type(y) 是布爾類型,則返回 x == ToNumber(y) 的結果。

仔細分析例子,首先:

varx=true;vary="42";x==y;//false

Type(x) 是布爾值,所以 ToNumber(x) 將 true 強制類型轉換為 1,變成 1 == "42",二者的類型仍然不同,"42" 根據規則被強制類型轉換為 42,最後變成 1 == 42,結果為 false。

對象和非對象之間的相等比較

關於對象(對象 / 函數 / 數組)和標量基本類型(字符串 / 數字 / 布爾值)之間的相等比較,ES5 規範 11.9.3.8-9 做如下規定:

如果 Type(x) 是字符串或數字,Type(y) 是對象,則返回 x == ToPrimitive(y) 的結果。
如果 Type(x) 是對象,Type(y) 是字符串或數字,則返回 ToPromitive(x) == y 的結果。
什麼是 toPrimitive() 函數?

應用場景:在 JavaScript 中,如果想要將對象轉換成基本類型時,再從基本類型轉換為對應的 String 或者Number,實質就是調用 valueOf 和 toString 方法,也就是所謂的拆箱轉換。

函數結構:toPrimitive(input, preferedType)

參數解釋:

input 是輸入的值,即要轉換的對象,必選。

preferedType 是期望轉換的基本類型,他可以是字符串,也可以是數字。選填,默認為 number。

執行過程:

如果轉換的類型是 number,會執行以下步驟:

如果 input 是原始值,直接返回這個值。
否則,如果 input 是對象,調用 input.valueOf(),如果結果是原始值,返回結果。
否則,調用input.toString()。如果結果是原始值,返回結果。
否則,拋出錯誤。如果轉換的類型是 string,2 和 3 會交換執行,即先執行 toString() 方法。

valueOf 和 toString 的優先級:

進行對象轉換時 (alert(對象)),優先調用 toString 方法,如沒有重寫 toString 將調用 valueOf 方法,如果兩方法都不沒有重寫,但按 Object 的 toString 輸出。
進行強轉字符串類型時將優先調用 toString 方法,強轉為數字時優先調用 valueOf。
在有運算操作符的情況下,valueOf 的優先級高於 toString。

由此可知,若 x 為對象時,我們改寫 x 的 valueOf 或 toString 方法可以讓標題的等式成立:

constx={val:0,valueOf:()=>{x.val++returnx.val},}

或者:

constx={val:0,toString:()=>{x.val++returnx.val},}

給對象 x設置一個屬性 val並賦值為 0,並修改其 valueOf、toString 方法,在 「x == 1 && x == 2 && x == 3」判斷執行時,每次等式比較都會觸發 valueOf、toString 方法,都會執行 val++ ,同時把最新的 val 值用於等式比較,三次等式判斷時 val 值分別為 1、2、3 與等式右側的 1、2、3 相同,從而使等式成立。

看下運行結果,果不其然,真想給自己點個讚。當然,讓標題的等式成立的方法肯定不止這一種,留言區期待你的回覆~

參考文檔:

《你不知道的 JavaScript(中卷)》

《== 和 === 區別》(https://blog.csdn.net/yyychocolate/article/details/108089477) 作者:Bliss_妍

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()