上次总结了一些强制类型转换的问题,今天来讲一讲与之相关的一个知识点:宽松相等和严格相等。
一般说法是“==检查值是否相等,===检查值和类型是否相等”,这个说法咋一听没有问题,但是还不够严谨,正确的解释应该是:“==允许在相等比较中进行强制类型转换,而===不允许”。
概述
首先有两个特例要牢记:
- NaN 不等于 NaN
- +0 等于 -0
还规定:两个对象(包括数组和函数)指向同一个值时即视为相等,不发生强制类型转化;==在比较两个不同类型的值时会发生隐式强制类型转化,会将其一或者两者都转换为相同的类型之后再进行比较。
字符串和数字之间的相等比较
规定:字符串和数字比较,先将字符串转换为数字之后再进行相等比较。1
2
3
4
5var a = 42;
var b = '42';
a === b; // false
a == b; // true
字符串b会先转换为数字之后再跟a进行比较。
其他类型和布尔类型之间的相等比较
规定:其他类型和布尔类型比较,先将布尔类型转换为数字之后再进行比较。==最容易出错的地方就是true和false与其他类型的比较:1
2
3
4
5
6var a = '42';
var b = true;
var c = false;
a == b; // false
a == c; // false
字符串a既不是true也不是false,但字符串a确实是个真值。注意,这里的==比较并不涉及对于a的ToBoolean操作,所以字符串a是真值还是假值跟 == 本身没有关系!
强烈建议:无论什么情况下都不要使用 == true 和 == false。这里面会有很多坑,一定不要手贱使用这种比较方式。
null和undefined之间的相等比较
规定:在 == 中 null 和 undefined 相等(它们也跟其自身相等),除此之外其他值都不存在这种情况。1
2null == undefined; // true
undefined == null; // true
1 | var a = null; |
平时代码判断中:1
2
3
4var a == doSomething();
if(a == null){
//..
}
条件判断 a == null 只有在 doSomething 返回 null和undefined 时才成立,除此之外其他值都不成立,包括 0、false和""这样的值。
如果使用显式判断,就要使用严格相等 ===1
2
3
4var a == doSomething();
if(a === null || a === undefined){
//..
}
对象和非对象之间的相等比较
规定:字符串或数字与对象比较,先将对象进行ToPrimitive操作后再进行比较。
注意,布尔类型与对象比较,根据上面定义的规则,布尔类型会转换为数字,再进行比较。1
2
3
4
5var a = 'abc';
var b = Object(a); //和 new String(a) 一样
a === b; // false
a == b; // true
a == b 结果为true,因为 b 通过 ToPrimitive 操作进行强制类型转换,并返回标量基本类型值 ‘abc’,与 a 相等。
还有一些特例:1
2
3
4
5
6
7
8
9
10
11var a = null;
var b = Object(a); // 和Object()一样
a == b; // false
var c = undefined;
var d = Object(c); // 和Object()一样
c == d; // false
var e = NaN;
var f = Object(e); // 和 new Number(e) 一样
e == f; // false
因为没有对应的封装对象,所以null和undefined不能够被封装,Object(null)和Object()均返回一个常规对象。NaN能够被封装为数字封装对象,但拆封之后 NaN == NaN 返回false,因为NaN不自等。
一些特例及比较策略
有一些比较难绕的例子,列出来希望能记住:1
2
3
4
5
6
7"0" == false; // true
false == 0; // true
false == ""; // true
false == []; // true
"" == 0; // true
"" == []; // true
0 == []; // true
以上有四种涉及 == false,我们说过要避免,所以不难掌握。我们总结两条比较规则,就能避开以上奇葩的情况:
- 如果两边的值中有
true或false,千万不要使用==。 - 如果两边的值中有
[]、""或0,尽量不要使用==,可以使用===。
注意:相等比较不要跟if(..){..}中条件判断式弄混,如if([]){ .. }中,[]是真值,跟false == []是不同的概念,不要弄晕!
抽象关系比较
这里简单介绍一下 a < b这种大小比较。
规则:
- 比较双方先调用
ToPrimitive,如果出现非字符串,就根据ToNumber规则将双方强制类型转换为数字进行比较。
1 | var a = [ 42 ]; |
- 如果比较双方都是字符串,则按字母顺序来进行比较。
1 | var a = [ "42" ]; |
a和b并没有转换成数字,这里ToPrimitive返回的是字符串,进行了字符串的比较,’0’小于’4’。
1 | var a = [4,2]; |
这里a转换为”4,2”,b转换为”0,4,3”,字符串比较。
1 | var a = { b: 42 }; |
这里 a和b都转换成了 [Object Object],故a<b不成立,这里要注意,看下面的例子:1
2
3
4
5
6
7
8var a = { b: 42 };
var b = { b: 43 };
a < b; // false
a == b; // false 相等操作的话要看上面!
a > b; // false
a <= b; // true
a >= b; // true
注意最后两条,根据规定:a <= b会被处理为 b < a 然后将结果反转。因为 b < a 的结果是 false,所以 a <= b 的结果是 true。
JavaScript中 <= 是不大于的意思,即 !(a > b),处理为 !(b < a)。同理,a >= b处理为 b <= a。
相等比较有严格相等,但关系比较却没有“严格关系比较”,也就是说要避免 a < b 中发生隐式强制类型转换,为了保证安全,应该对关系比较中的值进行显式强制类型转换:1
2
3
4var a = [ 42 ];
var b = "043";
a < b; // false 字符串的比较
Number(a) < Number(b); // true 数字比较