全等算符的迷思
因為 javascript 的相等算符 ==
會做很多奇怪的類型轉換,
所以不少人鼓吹應該全面使用全等算符 ===
。
但這是不必要的,因為那些奇怪的類型轉換,
只會發生在糟糕的 code style;
你根本不應該嘗試比較二個類型不相同的變數。
就像鼓吹結尾要加分號的作法,
我認為那是一種過度要求。
比較算符的問題
javascript 的 ==
會在比較二種類型不同的變數時,
做類型轉換,所以會造成一些 奇怪的結果 。
[1,2,3] == '1,2,3' // true
[] == ![] // true
甚至還有人 整理了一個表格 。
所以很多人鼓吹應該全面使用嚴格相等算符 ===
。
人的問題
但這不應該是全等算符的問題,而是人的問題。 基本上那些奇怪的狀況都不應該發生在你寫的 code 裡; 我的意思是你根本不應該試著那樣比較。
請問什麼時候會你會用 ==
比較二個陣列相不相等?
甚至是比較一個陣列和一個字串相不相等?
如果要比較二個陣列一不一樣,
應該用 array.any
之類的,一一檢查每個元素吧,
如果要檢查陣列為不為空,應該要直接檢查長度吧。
const a = [1,2,3]
const b = [1,2,3]
a.any((x, i) => x == b[i]) // 比較每個元素相不相等
a.length == 0 // 檢查陣列為空
更別提那些告訴你把物件和字串比較會發生什麼鬼事的人, 哪個瘋子會比較物件和字串?他期望得到什麼結果?type error 嗎?
真實的比較情況
基本上,雖然 javascript 是弱類型的, 你也不需要顯示標出變數的類型, 但你寫 javascript 的時候還是會清楚從上下文知道, 現在這個變數應該是什麼類型。
基本上你不會比較物件相不相等, (只有很少見的情況,比較物件相等就像 比較 C 裡的二個指標是不是指向同一個地方。) 多半你比較二個物件的某個原始類型屬性相不相等, 而比較原始類型,雖然也有些奇怪的地方, 但你應該只比較相同類型的屬性。
就算是字串和數字,你應該要顯式的類型轉換再比較,
除非你很懶, ==
可以自動幫你轉,
但我還是不建議這麼做,有時會讓人誤會變數的類型。
原始類型的雷
如果你預期輸入的數字可能不是數字, 那你應該做顯式類型轉換轉成數字:
n = Number(n)
另外也不要比較一個變數和 true false 相不相等, 直接放在 if 的括號裡就可以了。 如果要這樣比,建議先顯式轉為布林值:
if (thing == true) void 0 // naver do this!
if (thing) void 0 // just do this
if (Boolean(thing) == true) void 0 // or this
多型
很少數你會不知道變數型別的例子是, 你希望你的函數能做很多事, 所以寫了一個能傳入不同類型參數的多型函數。 那你要做的第一件事應該是先檢查傳入的參數到底是什麼類型, 而不是什麼都不管就亂比較一通。
function justLength(thing) {
// 處理不能存取屬性的 null 和 undefined
if (thing == null) return 0
// 處理布林,true = 1, false = 0
else if (typeof thing == 'boolean') return Number(thing)
// 處理字串與陣列
else if (typeof thing.length == 'number') return thing.length
else if (typeof thing == 'object') return Object.keys(thing).length
else throw new TypeError('unknown type, can not convert to number')
}
效率
有些人認為全等算符效率較好,可能是真的,但也不會差太多。 試想比較相同類型與不同類型的場合:
- 不管是相等或全等算符,都得先檢查二者類型相不相同。
- 全等要確認相同才能繼續比較,
- 相等要確認相同才不進行類型轉換。
- 然後才能比較內容是不是一樣。
因此對相同類型來說,不管是全等或相等, 比較都是二步:檢查類型,比較內容。 (雖然都是二步一樣的內容, 但相等可能會因為 可能會進行的類型轉換 , 而效率較低。)
至於不同類型,我已經說過了, 你不應該比較不同類型的變數; 除非你很清楚你在做什麼 。
這種時候,全等算符什麼都不做,只會丟 false 給你; 相等算符則會幫你完成多餘的工作, 然後給你一個很有個性的答案。
結論
我是很懶的人,知道分號規則後從不寫分號,
也很討厭 ===
要多打一個等號。
要用二個等號來表達比較已經夠反直覺了,
三個等號是什麼鬼?
lisp 中要比較就直接用 (= a b)
或 (eq a b)
就好,
賦值則是用 (setf a b)
;
但 javascript 因為用了等號賦值,只好把相等改成二個等號,
全等只好再降級成三個等號。
故事說完了, 結論是只做類型相同的比較時,二者幾無差別。 無論你是打算繼續用全等, 還是開始用相等,你都更認識了二者。 你做好決定了嗎?