JavaScript:(a==1 && a==2 && a==3)能輸出true麼?

laihuamin發表於2018-01-22

如果你能確切的答出可以,那恭喜你,你可以繞道了

前言

有人會說,這個問題好奇葩,放在別的語言裡,這要是能輸出true,估計是見鬼了,但是你別說,放在js中好真有可能。最近在一個人的推特上提了一個問題:

  • 問題:Can (a==1 && a==2 && a==3) ever evaluate to true?
  • 答案:yes

JavaScript:(a==1 && a==2 && a==3)能輸出true麼?

在這篇文章中,我將解釋這段程式碼的原理:

const a = {
  num: 0,
  valueOf: function() {
    return this.num += 1
  }
};
const equality = (a==1 && a==2 && a==3);
console.log(equality); // true
複製程式碼

你可以開啟chorme瀏覽器,然後開啟開發者模式,在console中輸入這段程式碼,你就可以看到輸出結果([windows]: Ctrl + Shift + J [mac]: Cmd + Opt + J)

有什麼竅門呢?

其實也沒有,能有的就是js中的兩個概念:

  • 隱式轉換
  • object的valueOf函式

隱式轉換

注意:這題裡面我們用的是==而不是===,在js中==代表的是等於而不是全等,那麼就存在變數的隱式轉化問題。這就意味著結果會比我們所期望的更多的可能性。對於js的隱式轉化,真的有很多文章,我推薦一下以下幾篇部落格,如果你想要了解,可以點進去:

推薦部落格

valueOf

JavaScript提供了一種將物件轉化為原始值的方法:Object.prototype.valueOf(),預設情況下,返回正在被呼叫的物件。

我們舉個例子:

const a = {
  num: 0
}
複製程式碼

我們可以對上述物件使用valueOf方法,他會返回一個物件。

a.valueOf();
// {num: 0}
複製程式碼

是不是很酷,我們可以用typeOf來檢測一下這個輸出結果的型別:

typeof a.valueOf();
// "object"
複製程式碼

為了讓valueOf可以更方便將一個物件轉化成原始值,我們可以重寫他,換種說法就是我們可以通過valueOf來返回一個字串、數字、布林值等來代替一個物件,我們可以看以下程式碼:

a.valueOf = function() {
  return this.num;
}
複製程式碼

我們已經重寫了原生的valueOf()方法,當我們呼叫valueOf的時候,他會返回a.num。那我們現在執行以下:

a.valueOf();
// 0
複製程式碼

我們得到0了,這很合理,因為0就是賦給a.num的值。那我們可以來做幾個測試:

typeof a.valueOf();
// "number"

a.num == a.valueOf()
// true
複製程式碼

很好,但為什麼這個很重要呢?

這很重要,因為當你兩種不同型別的遇到相等操作符的時候,js會對其進行型別轉化——它企圖將運算元的型別轉化為類似的。

在我們的問題中:(a==1 && a==2 && a==3)JavaScript會企圖將物件轉化成數字的型別,進行比較。當要轉化的是一個Object的時候,JavaScript會呼叫valueOf()方法。

自從我們改變了valueOf()方法之後,我們能不能做到以下幾點呢:

a == 0

// true
複製程式碼

我們做到了,異常輕鬆。

現在我們需要做的的一點是:當我們每次去呼叫a的值的時候,能改變它。

幸運的是,在JavaScript中有+=符號。

+=這個運算子可以輕鬆的去改變一個的值,我們可以舉個簡單的例子:

let b = 1
console.log(b+=1); // 2
console.log(b+=1); // 3
console.log(b+=1); // 4
複製程式碼

正如你所見的,我們每次使用加法賦值運算子,可以讓我們的變數增加。

所以我們可以將這個觀念用到valueOf()中。

a.valueOf = function() {
  return this.num += 1;
}
複製程式碼

當我們每次呼叫valueOf的時候,他會將變數增加1返回給我們。

隨著這個改變,我們來執行下面的程式碼:

const equality = (a==1 && a==2 && a==3);
console.log(equality); // true
複製程式碼

這就是它的工作原理。

記住下面兩點:

  • 使用相等操作符,js會做強制型別轉化
  • 我們的物件每次呼叫valueOf()它的值會增加1

所以比較的時候我們每次都能得到true。

  • 補充第二點的運算過程
a                     == 1   -> 
a.valueOf()           == 1   -> 
a.num += 1            == 1   -> 
0     += 1            == 1   ->
1                     == 1   -> true
a                     == 2   -> 
a.valueOf()           == 2   -> 
a.num += 1            == 2   -> 
1     += 1            == 2   ->
2                     == 2   -> true
a                     == 3   -> 
a.valueOf()           == 3   -> 
a.num += 1            == 3   -> 
2     += 1            == 3   ->
3                     == 3   -> true
複製程式碼

總結

謝謝你觀看這個小實驗,希望你能從中學到東西,有興趣的朋友也可以去我的github點個star,你的支援是我持續輸出的動力,謝謝!!!

相關文章