JavaScript 有趣的冷知識 :神奇的比較運算子(上)

Andy Chen
Andy的技術分享blog
7 min readJul 9, 2020

前言

繼上次介紹完神奇的 NaN 之後,今天要來介紹另一個主題:比較運算子。

在 JavaScript 中比較運算子可是擁有你想不到的特性,今天這篇文章就帶各位讀者好好來玩這個比較運算子吧!

相等比較

在 JavaScript 中相信大家都對 == 以及 === 又愛又恨吧!到底這兩種相等比較差在哪呢?

筆者最常聽到的版本是: ===== 多一個型別比較,所以 "123" == 123true,但 "123" === 123false,但 == 真的不會做型別比較嗎?

其實是會的,這邊筆者附上 ECMAScript 的規範文件。

讀者可以看第一行,會發現其實 == 是會進行型別比較的,而且假如兩者型別一樣是會進行 === 的比較,那 ===== 究竟是差在哪呢?一樣我們看個 ECMAScript 的規範文件。

可以看到 === 做的事情比 == 少很多,而且不曉得讀者們有沒有發現一個最重要的差別, == 除了進行型別比較外還會進行型態轉換而 === 只是單純的型別比較而已,所以如果要用一句話來總結 ===== 的差別,筆者覺得可以這樣回答:進行比較前有沒有將變數進行型態轉換。

神奇的相等比較

讀者有了上面相等比較的基礎觀念後,接下來筆者就要進入這篇文章第一個重頭戲了:如何讓一個變數同時等於 1、2、3、…。

相信讀者看到這段話一定會覺得很莫名其妙,怎麼可能會有一個變數同時等於那麼多個值,筆者一開始看到這段也覺得很莫名其妙但深入了解後才知道原來在 JavaScript 的世界裡真的什麼怪事都會發生XD

接下來就帶讀者看一段程式碼:

const a = {    
value: 0,
valueOf: function() {
return this.value += 1
}
}

讀者可能會覺得很奇怪,上面的程式碼跟相等比較無關啊,這邊筆者要先跟讀者介紹 JavaScript 是如何進行物件轉型,上面的相等比較有提到 == 會先進行型別比較之後若不一樣再進行轉型,而物件在進行轉型的時候會利用 valueOf()toString() 這兩種方法進行轉型。

由於我們要進行比較的不會是字串,所以這時候就可以偷偷對 valueOf() 進行加工,這邊要注意絕對不能把 valueOf()callback 用箭頭函式進行撰寫,因為箭頭函式並沒有 this 所以如果要拿到物件裡面的值就會找不到。

而加工的方法也很簡單,只要把物件內的值在呼叫 valueOf() 的時候進行加 1 的動作即可,這樣每次在呼叫這個物件的時候就會執行 valueOf() 內的 callback 進而達到每次的值都會加 1。

所以假如今天有人問你如何讓一個變數同時等於 1、2、3、…,這時候就可以這樣回答:

const a = {    
value: 0,
valueOf: function() {
return this.value += 1
}
}
console.log(a == 1 && a == 2 && a == 3) // true

這時候聰明的讀者應該就會想到破解方法,我不要用 == 就好啦!把 == 改成 === 就會回傳 false 了,因為 === 不會進行自動轉型的動作,所以在比較的時候就會因為 Object TypeNumber Type 不一樣而回傳 false

const a = {    
value: 0,
valueOf: function() {
return this.value += 1
}
}
console.log(a === 1 && a === 2 && a === 3) // false

但你以為這樣就沒有用了嗎?起初筆者也以為這樣就可以完美地避免這種結果發生,但只能說筆者真的太年輕了,在 JavaScript 的世界中總是有辦法可以讓這種看似不可能會發生的事情變成可能XD

神奇的嚴格比較

接下來再帶大家看另一段程式碼:

Object.defineProperty(object, 'property', descriptor)

這邊筆者要跟讀者介紹 JavaScript 用來定義或修改物件屬性的方法: Object.defineProperty() ,這個方法需要傳入三個參數分別是:objectpropertydescriptor,接下來筆者就稍微介紹這三個屬性的作用。

  • object:要修改的物件,會是 object type
  • property:要修改物件中的哪個屬性,這邊的屬性會寫成 string type
  • descriptor:要用哪些內容去修改這個屬性,會是 object type,這邊筆者稍微提醒一下讀者介紹並不是隨便一個 key 都可以讓 descriptor 使用,想知道所有 descriptor key 可以點擊這個連結,這邊筆者就不加以贅述了。

這邊筆者要用到的 descriptor key getget 顧名思義就是用來做 getter 的意思,透過 getter 就可以快速的取得該屬性的值,所以 get 這個 key 所配對的 value 必須要是一個 callback,接下來筆者就要來魔改這個 getter 了。

方法也很簡單首先要宣告一個物件以及一個變數,之後只要在 getter 裡面不斷的把這個變數做加 1 的動作,這樣之後再呼叫這個物件的屬性時就會不斷的被改變,同時也因為 getter 回傳的是 Number type 所以就算用嚴格比較也不會出錯,最終寫法就會像這樣:

const obj = { a: 0 }
let value = 0
Object.defineProperty(obj, 'a', {
get: function() {
return value += 1
}
})
console.log(obj.a === 1 && obj.a === 2 && obj.a === 3) // true

成功了💪,終於可以讓一個變數用嚴格比較也可以達到同時等於多個值,但相信讀者應該會覺得底下的判斷式還是有點醜,難道沒有辦法讓嚴格比較的判斷式長得跟相等比較差不多嗎?

這邊要用到 window 的觀念,我們都知道 JavaScript 的 window 其實是一個物件,只要是新增到 window 物件內的屬性我們都可以直接拿來用,不用刻意寫成 object.property 的樣子,也因為 window 是一個物件,所以我們可以直接在這個物件內新增自己想要的屬性。

有了上面這些觀念後就可以稍微改變 Object.defineProperty() 的內容了,這時候我們只要把 object 替換成 window 即可,剩下的都不用變動,寫法就像底下這樣:

let value = 0Object.defineProperty(window, 'a', {     
get: function() {
return value += 1
}
})
console.log(a === 1 && a === 2 && a === 3) // true

成功了💪,而且也不用寫成 object.property 的方式,再加上又是嚴格比較,所以以後可以正大光明的跟別人說:不管用什麼樣的比較方式我都可以讓一個變數同時等於多個值了XD

小結

這次文章介紹了 JavaScript 的相等比較以及嚴格比較,相信讀者們應該會覺得 JavaScript 真的是個很有趣的程式語言吧XD

其實這次的比較運算子還沒完全講完,所以筆者拆成上下兩篇,絕對不是為了拖台錢跟賺文章篇幅XD

下一篇文章一樣會帶大家看其他神奇的比較運算子,如果有什麼問題歡迎在下面留言給筆者,沒問題的話就下一篇文章見嘍~

--

--

Andy Chen
Andy的技術分享blog

嗨嗨我是Andy,用嘴巴工作的工程師😂,喜歡學習不同領域的內容,專長為網頁開發,歡迎大家跟我聊技術~