不應該
對任何 non-Primitive 做「任何比對」[] == ![] // true WAT ???!
你不應該 對 non-Primitive 做「任何比對」,所以這題不講。 (X)
當然不會不講,不過其實邏輯上沒有意義。
所以真的可以不用講。 (?)
真實世界的邏輯上來說,沒有人會把 Array 去比對
一個 negative self Array。
(題外話,有人知道 negative self Array 到底要表示什麼嗎 XDDDD)
有這一題是因為,很多反對使用一般相等 ( == )
的人都是用這個例子來說明 JavaScript 不應該使用 一般相等 ( == ),
但如果你把 == 視作 抽象
等價性,non-Primitive 並沒有被抽象
的意義。
舉個實際例子:
同樣的,如果你今天如果想要做的事情是,比對 兩個 Array 是不是一樣
,應該考慮 line 8A != B
<===> ! ( A == B )
line 4 表示,你要比對一個 Array 去比對
一個 negative Array (??????)
其實你在做 Coercion , Coercion 之後才比對。
雖然兩種都不適合寫在 JavaScript ,但是其背後邏輯、意義完全不一樣,可以思考一下再往下看。
[] == ![]
line 4 : 做 ToBoolean 的 Coercion 運算,是查表
,很顯然 [] 不是 falsy,ToBoolean([]) 回傳是 true 。
![] ==> !(true) => false
參考之前文章
[day08] YDKJS (Coercion:spec) : 回來讀 spec. ToString(), ToNumber() , ToBoolean()
[day09] YDKJS (Coercion:spec) : Coercion 型別轉換 、 Boxing Wrappers
昨天有提到
Coercive Equality : string, number, boolean
。
[day11] YDKJS (Equality) : 一般相等 == 比較「值」 , 嚴格相等 === 比較 「型別和值」是錯的?
再複習一下 ToNumber() :
- 空字串變成
0
- false -> 0 , true -> 1
reference:
[day08] YDKJS (Coercion:spec) : 回來讀 spec. ToString(), ToNumber() , ToBoolean()
[] != []
其實等於 ! ([] == [])
,
然後型別一樣( top level objects type),做嚴格相等。!([] === [])
這邊之後的運算就不是 抽象等價性 (==) 的鍋,
原本 JavaScript 真的不適合來比對 non-Primitive,
你用 嚴格相等 === 還是不適合,因為 non-Primitive 做比較通常都有 reference share 的問題 。
!(false) // true
。boolean 最好也不要用在 condition ,之前有提過 condition 要寫
非常具體
的如 array.length > 0。
假設你要寫的是 line 3
,雖然我不建議你這樣寫
:
[] == true // [] ToPrimitive()
也就是"" == true
,
其中一邊是 boolean , boolean 還要再做 ToNumber()"" == 1
。
出現數字,另一邊空字串 ToNumber()0 == 1
。
同數字型別,做 嚴格相等 === 。
回傳 false。
還是再三強調,如果這邊用 嚴格相等 (===)
,回傳也是 false 。
不過原因是因為型別不相等
回傳 false ,而不是比對相等失敗回傳 false。
抽象等價性 (==)
在 0
(數字 0)、 ""
(空字串) " "
(可以透過 Coercion 轉換成等效空字串的任何字串)。抽象等價性 (==)
不要用 == false
or == true
,會觸發多次 Coercion 。型別是 boolean 且值 true or false
(=== true\false),才考慮不會處發 Coercion 的嚴格相等 (===) ,否則你得到 false 多數狀況是因為型別不一樣回傳得 false。這部分其實是 Kyle Simpson 的論證,也是一種看待 JavaScript 不同的 Mindset。
我「個人偏好」說,這是洗腦時間,所以我把文字都保留下來。
在所有可能的地方,你都應該首選 == 。
瞭解型別總比不瞭解它們更好。
程式碼的不確定性是使程式碼難以閱讀的原因,它使程式碼容易受到意外的錯誤的影響。
有些人說 因為我不知道型別
,所以我需要使用靜態型別,例如TypeScript等。
但是,== 並不是要與未知型別進行比較,這不是它的用途。
相反的,他要限定 null, undefined, string, number, boolean。
這個和很多人的認知相反。
大多數人,當他們不知道型別時,會使用==,然後陷入錯誤中。
所以,當您不知道型別時,切勿使用==。僅在知道型別時才使用==。
== 適用於知道型別,且要使用型別轉換(Coercion) 。
(我個人稱 抽象等價性 (==)
)
這個 TypeScript 提示其實是 JavaScript 本來就有的規則。
也是前面一直強調 嚴格相等( === ) 型別不同一定回傳 false
,
但這個提示如果你很熟悉 JavaScript 規則,其實沒必要,
因為有些時候 「你故意想比對型別
,然後才比對值
」。
(但多數人都使用 typeof 去比對,沒有好好使用工具。)
如果您知道比較中的型別,並且它們有所不同,據說
使用===可以保護您並保護您,但不同的 type 一定會回傳 false (像是 TS 提示一樣,broken)。
這邊主要是說,如果你要用多個 嚴格相等( === ) 達到
抽象等價性 (==)
的目的,
其實多個 嚴格相等( === ) 再做 && 運算子 會比抽象等價性 (==)
的內部 Coercion 還要慢 (雖然這邊的慢是 microseconds 可以忽略)。比如昨天的例子:
在大多數情況下,讀者沒有必要看到它們,
透過非常明顯的條列嚴格相等(===),並用 && 運算子連接,會分散他們的注意力。
而同樣的狀況之下,使用更正確的抽象等價性
,即使用 == ,會使 code 更乾淨。
記住,如果您知道型別,這些敘述才是對的,只有在您知道型別的情況下,才提出這些聲明。
這可能是不合理的要求。因為誰都無法保證100%知道整個系統中的型別,這是不合理的。
因此,當比較發生
的時候,有時候存在一些不確定性。
Kyle Simpson 的主張是,那應該是一種少數狀況
(少數狀況使用 === ),而不是常態。
它的意思是,無論是少數狀況還是常態,由於不確定性(型別不確定)的緣故,這部分程式碼一定更難理解
。
所以乾脆在這個時候 重構(refactor) 你的 code
。
如果你真的不確定你的型別,就用 嚴格相等( === ) 直接告訴讀 code 的人。
因為 嚴格相等( === ) 是最容易表現不確定型別
的比較,且不會有 Corner Cases。
(個人註:但你最好補上註解,這時候寫註解是最恰當的,因為沒有型別的 code 真的很難理解。)
真的任何方法都不能確定型別,才使用 嚴格相等( === )。
大概解釋:
反正你能用 == 的狀況下(前面提的狀況),用 == 一定比較合適。
(通常是抽象或是要刻意做 Coercion 型別轉換。)
所以看到 === ,其實代表你不知道型別。
講那麼多,無論你是用 Kyle Simpson 的 mindset (使用 JavaScript 的 == 代表明確型別 ),
或是其他工具如 Flow, TypeScript ,你都還是要知道你寫的 code 的型別
,否則都只能用 === (然後基本上都只回傳 false)。