面試官: (a==1 && a==2 && a==3) 能否在 JavaScript 中為“真”?
最近,我被問到一個非常有趣的面試問題:Can (a== 1 && a==2 && a==3) ever evaluate to true in JavaScript?。 我幾乎失去了工作機會,因為我無法回答。
那一刻,我被這個問題嚇了一跳,以為面試官在開玩笑。
但當我看到他的“微笑”時,一種“你一定不知道答案”的感覺掠過我的腦海,這絕對不是一個容易解決的問題。
文章將給出6個專業答案,讓我們馬上開始吧。
解決方案一:valueOf && toString
第一個解決方案非常簡單,相信你在閱讀此代碼后會有一個想法。
let a = {
name: 'fatfish',
toString () {
return 'medium'
}
}
if (a == 'medium') {
console.log('hello medium')
}
太神奇了,這是怎么回事? 別擔心,我的朋友,我會盡力解釋原因。
解釋部分隱式轉換規則
在 JavaScript 中使用 == 比較兩個值時,會執行以下操作:
- 將兩個比較的值轉換為相同的類型。
- 轉換后(等式的一側或兩側可以轉換),比較值。
比較規則如下表所示:
從表中可以得到一些信息。 為了使 (a == 1),a 只能是以下幾種情況:
- a 的類型是 String,可以轉換為數字 1('1' == 1 => true)。
- a 的類型是布爾值,可以轉換為數字 1 (true == 1 => true)。
- a的類型是Object,可以通過“轉換機制”轉換為數字1。
對象到原始類型的“轉換機制”
規則 1 和規則 2 沒有什么特別之處,我們來看看規則 3:
當對象轉換為原始類型時,會調用內置的 [ToPrimitive] 函數。
邏輯大致如下:
- 如果有 Symbol.toPrimitive 方法,則先調用。
- 調用valueOf,如果可以轉成原來的類型,則返回。
- 調用toString,如果能轉換成原來的類型,則返回。
- 如果沒有返回原始類型,則會報錯。
const obj = {
value: 1,
valueOf() {
return 2
},
toString() {
return '3'
},
[Symbol.toPrimitive]() {
return 4
}
}
obj == 4
我的朋友,感謝你非常耐心地閱讀了這么長時間,我相信你心中已經有了答案。
let a = {
i: 1,
valueOf() {
return this.i++
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('hello medium')
}
解決方案 2:數組 && 連接
數組對象的隱式轉換也符合規則 3,但會在“toString”之前調用“join”方法。 所以你可以從這里開始。
let a = [1, 2, 3]
a.join = a.shift
if (a == 1 && a == 2 && a == 3) {
console.log('hello medium')
}
解決方案 3:使用“with”運算符
MDN 有一個關于 with 使用的警告,好像它的存在是一個錯誤。 我在工作中從未使用過它,但它可以用來解決這個問題。
let i = 1
with ({
get a() {
return i++
}
}) {
if (a == 1 && a == 2 && a == 3) {
console.log('hello medium')
}
}
你太聰明了,甚至不需要我解釋代碼的含義。
解決方案 4:Symbol.toPrimitive
我們可以使用隱式轉換規則3來完成問題(看完答案你就知道為什么了!)。
const a = {
i: 1,
[Symbol.toPrimitive]() {
return this.i++
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('hello medium')
}
數據劫持也是一種出路
通過隱式轉換,我們做了3個答案讓a == 1 && a == 2 && a == 3 返回true,你一定想到了另一個答案,數據劫持,偉大的Vue我們用它來贏得人心 數百萬開發者,我們也嘗試用它來解決這個面試問題。
解決方案 5:Object.defineProperty
通過劫持‘window’對象,每次讀取‘a’屬性時,_a加1。
let _a = 1
Object.defineProperty(window, 'a', {
get() {
return _a++
}
})
if (a == 1 && a == 2 && a == 3) {
console.log('hello medium')
}
解決方案 6:代理
還有另一種劫持數據的方式,Vue3 也用 Proxy 替換了 Object.defineProperty。
let a = new Proxy({ i: 1 }, {
get(target) {
return () => target.i++
}
})
if (a == 1 && a == 2 && a == 3) {
console.log('hello medium')
}
?