JavaScript 有趣的冷知識 :神奇的比較運算子(上)
前言
繼上次介紹完神奇的 NaN 之後,今天要來介紹另一個主題:比較運算子。
在 JavaScript 中比較運算子可是擁有你想不到的特性,今天這篇文章就帶各位讀者好好來玩這個比較運算子吧!
相等比較
在 JavaScript 中相信大家都對 ==
以及 ===
又愛又恨吧!到底這兩種相等比較差在哪呢?
筆者最常聽到的版本是: ===
比 ==
多一個型別比較,所以 "123" == 123
是 true,但 "123" === 123
是 false,但 ==
真的不會做型別比較嗎?
其實是會的,這邊筆者附上 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 Type 跟 Number 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()
,這個方法需要傳入三個參數分別是:object、property、descriptor,接下來筆者就稍微介紹這三個屬性的作用。
- object:要修改的物件,會是 object type。
- property:要修改物件中的哪個屬性,這邊的屬性會寫成 string type 。
- descriptor:要用哪些內容去修改這個屬性,會是 object type,這邊筆者稍微提醒一下讀者介紹並不是隨便一個 key 都可以讓 descriptor 使用,想知道所有 descriptor 的 key 可以點擊這個連結,這邊筆者就不加以贅述了。
這邊筆者要用到的 descriptor key 是 get
, get
顧名思義就是用來做 getter 的意思,透過 getter 就可以快速的取得該屬性的值,所以 get
這個 key 所配對的 value 必須要是一個 callback,接下來筆者就要來魔改這個 getter 了。
方法也很簡單首先要宣告一個物件以及一個變數,之後只要在 getter 裡面不斷的把這個變數做加 1 的動作,這樣之後再呼叫這個物件的屬性時就會不斷的被改變,同時也因為 getter 回傳的是 Number type 所以就算用嚴格比較也不會出錯,最終寫法就會像這樣:
const obj = { a: 0 }
let value = 0Object.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
下一篇文章一樣會帶大家看其他神奇的比較運算子,如果有什麼問題歡迎在下面留言給筆者,沒問題的話就下一篇文章見嘍~