宽鬆相等 与 严格相等
宽鬆相等(==)允许相等比较中的强制转型,严格相等(===)则不允许强制转型。
如果比较的是相同型别的值,== 与 === 使用相同的演算法。
若比较的是不同型别的值,应该要确认,是否想要强制转型。
若想强制转型,就使用==相等性;若不想,则使用===相等性。
如果被比较的值具有相同的型别,它们就会以同一性(Identity)来比较。42只会等于42,"abc"只会等于"abc"。
少数例外:
NaN永远不等于自己+0 与 -0 相等比较object(function和array),只有在它们都指向同一个值的参考,才会相等。
使用==相等性来比较,若2边的型别不同,其中之一(或2者)会进行隐含地强制转型成相同型别,再进行值的同一性比较。
!=宽鬆不相等,反转==的比较结果;!==严格不相等,反转===的比较结果。
比较:字串与数字
a === b
会失败,因为===不允许转型。
a == b
成功,意味着,它们转型成相同的型别比较,那究竟是42转"42",还是"42"转42呢?
以ES5的规格指出,"42"会被强制转型成number再进行比较。
比较:boolean与其他值
若试着将一个值与true/false做比较,有可能出现陷阱。
根据ES5的规格,如果其中一方是boolean的话,那就会强制转型成number做比较。
Example:
x
是true(boolean),所以会强制转型成1(number),而"42"转型成42。1 == 42
的结果很明显是flase。
y=false
,会变成42 == 0
,结果也是flase。
问题来了,"42" == true
与"42" == false
结果都是false,那"42"究竟是什么?
实事上,"42"的确是truthy,问题是出在相等性比较式,"42"是truthy值,拥有true的行为,但不会被转型成true,而true会被转型成1。切记,"42"再怎么转型,永远都不会是true。
所以"42"是truthy或flasy,跟==运算一点关係都没有。
不管在任何情况下,都不要使用== true
或== flase
。
但=== true
或=== flase
不会强制转型,所以不会有这个问题产生。
var a = "42";//会失败if (a == true) {// ..}//会失败if (a === true) {// ..}//会成功,隐含转型if (a) {// ..}//更好,明确转型if (!!a) {// ..}//一样会成功if (Boolean( a )) {// ..}
比较:null与undefined
null与undefined,以==相等性进行比较,会等于彼此,也等于自身,而且不相等于其他值。
var a = null;var b;a == b;// truea == null;// trueb == null;// truea == false;// falseb == false;// falsea == "";// falseb == "";// falsea == 0;// falseb == 0;// false
null与undefined在==相等性的情况下,彼此的强制转型是安全的,不会有其他的值导致误判情形。
善用null与undefined的特性:
var a = doSomething();if (a == null) {// ..}
以上的情形,只有在doSomething()
回传null或undefined时,if判断式才会通过,即使是其他的false值也是失败。
var a = doSomething();if (a === undefined || a === null) {// ..}
使用===不允许强制转型的话,处理方式就没那么漂亮。
比较:物件与非物件
array是object,所以[42]会先强制转型成"42",再转成42,所以整个运算式会变成42 == 42。
容易引起麻烦的情况
"0" == false;// truefalse == 0;// truefalse == "";// truefalse == [];// true"" == 0;// true"" == [];// true0 == [];// true
以上的7种情况,都为true,我们来分析这7种情况。
"0" == false
,false => 0,"0" => 0false == 0
,false => 0false == ""
,false => 0," " => 0false == []
,false => 0,[ ] => 0"" == 0
," " => 0"" == []
," " => 0,[ ] => 00 == []
,[ ] => 0
疯狂的例子
[] == ![]; // true2 == [2]; // true"" == [null]; // true
怎么会是true呢?
[] == ![]
,!
运算子会先将[]
转型成false,所以实际相等性比较是[] == false
。
2 == [2]
,object会先转基型值,[2] => "2" => 2。
"" == [null]
,[null] => " " => 0。
安全的隐含强制转型
请思考 == 相等性,另一边可能会出现的值。
如果比较的任一边有 true
、false
、[ ]
、" "
、0
,就不要使用 ==
。
比较项目的图表,帮助记忆。
来源:https://github.com/dorey/JavaScript-Equality-Table
抽象的关係式比较
若遇到a < b
的情况,将2个运算元转型,若其中一方的结果不是string,那这2个值会强迫转成number,再进行数值的比较。
var a = [ 42 ];var b = [ "43" ];a < b;// trueb < a;// false
若2个值都是string,那就会进行字元的词典顺序比较。
var a = [ "42" ];var b = [ "043" ];a < b;// false
"42"会跟"043"逐字元作比较,第一个字元是"4"与"0"的比较,"0"在词典的顺序上小于"4",回传比较结果是false。
那这个呢?
var a = { b: 42 };var b = { b: 43 };a < b;//false
因为a
跟b
都会转成"[object Object]"
,所以比较上,没有谁大谁小的问题。
var a = { b: 42 };var b = { b: 43 };a < b;// falsea == b;// falsea > b;// falsea <= b;// truea >= b;// true
比较奇怪的是a == b
,既然都是"[object Object]"
,应该要一样才对啊,各位不要被刚刚的a < b
範例误导了。2个物件的比较,比的是「是否为同一个参考」。
依据规格,a <= b
,会先估算b < a
的结果(false),再否定其结果,所以a <= b
会是true。
<=
字面上的意思是「小于或等于」,但更精确的说法应该是「不大于」。所以a <= b
被解读为!(a > b)
再视为!(b < a)
。!false
等同于true
。
a >= b
先被视为b <= a
,再解读为!(b > a)
,再一次视为!(a < b)
。!false
等同于true
。
参考来源:
此为You Don't Know JS系列的笔记。