如何讓 x == 1 && x == 2 && x == 3 等式成立

如何讓 x == 1 && x == 2 && x == 3 等式成立

https://www.zoo.team/article/comparison-operation

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

話音剛落,筆者立馬失去意識(shí),雙眼一黑,兩腿一蹬,心里暗罵:什么玩意兒!

雖然當(dāng)時(shí)沒回答上來,但覺得這題非常有意思,便在這為大家分享下后續(xù)的解題思路:

寬松相等 == 和嚴(yán)格相等 === 都能用來判斷兩個(gè)值是否“相等”,首先,我們要明確上文提到的等于指的是哪一種,我們先看下二者的區(qū)別:

(1) 對(duì)于基礎(chǔ)類型之間的比較,== 和 === 是有區(qū)別的:

不同類型間比較,== 比較“轉(zhuǎn)化成同一類型后的值”看“值”是否相等,=== 如果類型不同,其結(jié)果就是不等

同類型比較,直接進(jìn)行“值”比較,兩者結(jié)果一樣。

(2) 對(duì)于引用類型之間的比較,== 和 === 是沒有區(qū)別的,都進(jìn)行“指針地址”比較。

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

因?yàn)轭愋筒煌?== 結(jié)果為 false

對(duì)于 ==,將引用類型轉(zhuǎn)化為基礎(chǔ)類型,進(jìn)行“值”比較。



“== 允許在相等比較中進(jìn)行強(qiáng)制類型轉(zhuǎn)換,而 === 不允許?!?br>
由此可見,上文提到的等于指的寬松相等 ==,題目變?yōu)?“x == 1 && x == 2 && x == 3”。

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

同類型數(shù)據(jù)之間的相等比較
如果 Type(x) 等于 Type(y)  ES5 規(guī)范 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 的數(shù)字值相同,返回 true。
如果 x 為 +0,y 為 -0,返回 true。
如果 x 為 -0,y 為 +0,返回 true。
如果 Type(x) 是 String,則如果 x 和 y 是字符的序列完全相同(相同的長(zhǎng)度和相同位置相同的字符),則返回 true。否則,返回 false。
如果 Type(x) 是 Boolean,則如果 x 和 y 都為 true 或都為 false,則返回 true。否則,返回 false。
如果 x 和 y 指向同一對(duì)象,則返回 true。否則,返回 false。
null 和 undefined 之間的相等比較
null 和 undefined 之間的 == 也涉及隱式強(qiáng)制類型轉(zhuǎn)換。ES5 規(guī)范 11.9.3.2-3 這樣定義:

如果 x 為 null,y 為 undefined,則結(jié)果為 true。
如果 x 為 undefined,y 為 null,則結(jié)果為 true。
在 == 中,null 和 undefined 相等(它們也與其自身相等),除此之外其他值都不和它們兩個(gè)相等。

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

var a = null;
var  b;
a == b; // true
a == null; // true
b == null; // true
a == false; // false
b == false; // false
a == ""; // false
b == ""; // false
a == 0; // false
b == 0; // false
字符串和數(shù)字之間的相等比較
ES5 規(guī)范 11.9.3.4-5 這樣定義:

如果 Type(x) 是數(shù)字,Type(y) 是字符串,則返回 x == ToNumber(y) 的結(jié)果。
如果 Type(x) 是字符串,Type(y) 是數(shù)字,則返回 ToNumber(x) == y 的結(jié)果。
var a = 42;

var b = "42";

a === b; // false

a == b; // true
因?yàn)闆]有強(qiáng)制類型轉(zhuǎn)換,所以 a === b 為 false,42 和 "42" 不相等。

根據(jù)規(guī)范,"42" 應(yīng)該被強(qiáng)制類型轉(zhuǎn)換為數(shù)字以便進(jìn)行相等比較。

其他類型和布爾類型之間的相等比較
ES5 規(guī)范 11.9.3.6-7 這樣定義:

如果 Type(x) 是布爾類型,則返回 ToNumber(x) == y 的結(jié)果。
如果 Type(y) 是布爾類型,則返回 x == ToNumber(y) 的結(jié)果。
仔細(xì)分析例子,首先:

var x = true;

var y = "42";

x == y; // false
Type(x) 是布爾值,所以 ToNumber(x) 將 true 強(qiáng)制類型轉(zhuǎn)換為 1,變成 1 == "42",二者的類型仍然不同,"42" 根據(jù)規(guī)則被強(qiáng)制類型轉(zhuǎn)換為 42,最后變成 1 == 42,結(jié)果為 false。






對(duì)象和非對(duì)象之間的相等比較
關(guān)于對(duì)象(對(duì)象 / 函數(shù) / 數(shù)組)和標(biāo)量基本類型(字符串 / 數(shù)字 / 布爾值)之間的相等比較,ES5 規(guī)范 11.9.3.8-9 做如下規(guī)定:

如果 Type(x) 是字符串或數(shù)字,Type(y) 是對(duì)象,則返回 x == ToPrimitive(y) 的結(jié)果。
如果 Type(x) 是對(duì)象,Type(y) 是字符串或數(shù)字,則返回 ToPromitive(x) == y 的結(jié)果。
什么是 toPrimitive() 函數(shù)?
應(yīng)用場(chǎng)景:在 JavaScript 中,如果想要將對(duì)象轉(zhuǎn)換成基本類型時(shí),再?gòu)幕绢愋娃D(zhuǎn)換為對(duì)應(yīng)的 String 或者 Number,實(shí)質(zhì)就是調(diào)用 valueOf 和 toString 方法,也就是所謂的拆箱轉(zhuǎn)換。

函數(shù)結(jié)構(gòu):toPrimitive(input, preferedType)

參數(shù)解釋:

input 是輸入的值,即要轉(zhuǎn)換的對(duì)象,必選。

preferedType 是期望轉(zhuǎn)換的基本類型,他可以是字符串,也可以是數(shù)字。選填,默認(rèn)為 number。

執(zhí)行過程:

如果轉(zhuǎn)換的類型是 number,會(huì)執(zhí)行以下步驟:

如果 input 是原始值,直接返回這個(gè)值。
否則,如果 input 是對(duì)象,調(diào)用 input.valueOf(),如果結(jié)果是原始值,返回結(jié)果。
否則,調(diào)用input.toString()。如果結(jié)果是原始值,返回結(jié)果。
否則,拋出錯(cuò)誤。如果轉(zhuǎn)換的類型是 string,2 和 3 會(huì)交換執(zhí)行,即先執(zhí)行 toString() 方法。
valueOf 和 toString 的優(yōu)先級(jí):

進(jìn)行對(duì)象轉(zhuǎn)換時(shí) (alert(對(duì)象)),優(yōu)先調(diào)用 toString 方法,如沒有重寫 toString 將調(diào)用 valueOf 方法,如果兩方法都不沒有重寫,但按 Object 的 toString 輸出。
進(jìn)行強(qiáng)轉(zhuǎn)字符串類型時(shí)將優(yōu)先調(diào)用 toString 方法,強(qiáng)轉(zhuǎn)為數(shù)字時(shí)優(yōu)先調(diào)用 valueOf。
在有運(yùn)算操作符的情況下,valueOf 的優(yōu)先級(jí)高于 toString。
由此可知,若 x 為對(duì)象時(shí),我們改寫 x 的 valueOf 或 toString 方法可以讓標(biāo)題的等式成立:

const x = {
  val: 0,
  valueOf: () => {
    x.val++
    return x.val
  },
}
或者:

const x = {
  val: 0,
  toString: () => {
    x.val++
    return x.val
  },
}
給對(duì)象 x設(shè)置一個(gè)屬性 val并賦值為 0,并修改其 valueOf、toString 方法,在 “x == 1 && x == 2 && x == 3”判斷執(zhí)行時(shí),每次等式比較都會(huì)觸發(fā) valueOf、toString 方法,都會(huì)執(zhí)行 val++ ,同時(shí)把最新的 val 值用于等式比較,三次等式判斷時(shí) val 值分別為 1、2、3 與等式右側(cè)的 1、2、3 相同,從而使等式成立。








看下運(yùn)行結(jié)果,果不其然,真想給自己點(diǎn)個(gè)贊。當(dāng)然,讓標(biāo)題的等式成立的方法肯定不止這一種,留言區(qū)期待你的回復(fù)~

參考文檔:

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

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

作者:小妖(筆名子謙)


歡迎關(guān)注微信公眾號(hào) :政采云前端團(tuán)隊(duì)