宽松相等(==)和严格相等(===)都用来判断两个值是否"相等",但是他们之间有一个很重要的区别,特备是在判断条件上。
==允许在相等比较中进行强制类型转换,而===不允许
1.相等比较操作的性能
==允许在相等比较中进行强制类型转换,所以有人会觉得==比===慢,实际上强制类型转换确实需要多花点时间,但仅仅是微秒级(百万分之一) 的差别而已,所以性能上差别微乎其微。
比较两个值时:
- 如果进行比较的两个值类型相同,则==和===使用相同算法,所以除了js引擎实现上的细微差别之外,它们之间没什么不同。
- 如果两个值的类型不同时,我们需要考虑有没有强制类型转换的必要,有就用==,没有就用===,不用在乎性能
注: ==和===都会检查操作数的类型。区别在于操作数类型不同时,它们的处理方式不同。
2.抽象相等
ES5规范11.9.3节的“抽象相等比较算法”定义了==运算的行为。该算法简单而全面,涵盖了所有可能出现的类型组合,以及它们进行强制类型转换的方式。
需要注意的点:
1.如果两个值的类型相同,就仅仅比较他们是否相等。
2.对象(包括函数与数组)之间进行比较时,两个对象指向同一个值时即视为相等,不发生强制类型转换。实际在比较两个对象的情况下,==和===的工作原理是一样的
非常规的情况:
- NaN不等于NaN
- +0等于-0
==在比较两个不同类型的时候会发生隐式强制类型转换,会将其中之一或者两者都转换为相同类型后再进行比较。
2.1 字符串和数组的比较
字符串与数组进行比较时会利用ToNumber抽象操作将字符串转化为数字然后在进行左右两边值的比较。
2.2 其他类型与布尔类型之间相等比较
其他类型与布尔类型比较时:使用ToNumber将布尔值强制转换为1(true)或者0(false)
重点:不管布尔类型与其他任何类型进行比较,==两边的布尔值都会被强制转换为数字。
注:“!!” ——两个叹号表示把目标值转化为布尔值,相当于使用Boolean()方法
2.3 null和undefined之间的比较
null和undefined之间的==也涉及隐式强制类型转换的功能。
在==中null和undefined相等(它们也与其自身相等)。初此之外与其他值比较都不会相等。这也就是说在==中null和undefined时一回事,可以相互进行隐式强制类型转换。null和undefined之间强制类型转换时安全可靠的。
可以通过这种方式将null和undefined作为等价值比较
var a = doSomething();
if(a==null){
//..
}
条件判断a==null仅在doSomething()返回null和undefined时才成立,初次之外其他值都不成立,包括0、false和''这样的假值。
2.4 对象和非对象之间的比较
关于对象(对象/函数/数组)和标量基本类型(字符串/数字/布尔值)之间相比较时,除了布尔值外,其他会调用ToPromitive抽象操作来处理对象的隐式强制类型转换。
为了将值转换为对应的基本类型,抽象操作ToPromitive会首先部操作DefaultValue)检查该值是否有valueOf()方法。如果有并且返回基本数据类型,就使用该值进行强制类型转换。如果没有就使用toString()的返回值(如果存在)来进行强制类型转换。如果valueOf()和toString()均不返回基本类型值,会产生TypeError错误。
"拆封"即"打开"封装对象(如new String("abc")),返回其中基本数据类型值("abc")。==中ToPromitive强制类型转换也会发生这样的情况:
var a = "abc";
var b = Object(a);//与new String(a)一样
a===b;//false
a===b;//true
a==b结果为true,因为b通过ToPromitive进行强制类型转换,并返回标量基本类型值"abc"与a相等。
但有一些值不这样,原因是==算法中其他优先级更高的规则。
var a = null;
var b = Object(null);//和Object()一样
a==b;//false
var c = undefined;
var d = Object(c);//和Object()-样
c==d;//false
var e = NaN;
var f = Object(e);//和Number(e)一样
e==f;//false
因为没有对应的封装对象,所以null和undefined不能够被封装(boxed),Object(null)和Object(undefined)均返回一个常规对象。
NaN能够被封装为数字封装对象,但拆封后NaN==NaN返回false,因为NaN不等于NaN
3.比较少见的情况
==中隐式强制类型转换已经介绍完,但是也有些需要特别注意和避免的比较少见的情况
首先来看看更改内置原生原型会导致哪些奇怪的结果。
3.1 返回其他数字
Number.prototype.valueOf = function(){
return 3;
}
new Number(2)==3;//true
2==3不会有这种问题,因为2和3都是数字基本类型值,不会调用Number.prototype.valueOf()方法。而Number(2)涉及到用ToPrimitive强制类型转换,因此会调用valueOf()。
if(a==2&&a==3){
//...
}
你或许觉得上述判断不可能为true,因为a不会同时等于2和3。但是“同时”一词并不准确,因为a==2在a==3之前执行。
如果让a.valueOf每次调用都产生副作用,比如第一次返回2,第二次返回3,就会出现这样的情况。这实现起来很简单:
var i = 2;
Number.prototype.valueOf = function(){
return i++;
}
var a = new Number(42);
if(a==2&&a==3){
console.log("yep,this happened.")
}
再此强调,千万不要这样,也不要因此来抱怨强制类型转换。对一种机制的滥用并不能成为诟病他的借口。我们应该正确合理的运用强制类型转换机制,避免这些极端的情况。
3.2 假值的相等比价
==中的隐式强制类型转换最为人诟病的地方是假值的相等比较。
下面分别列出常规和非常的情况:
"0" == null;//false
"0" == undefined;//false
"0" == false;//true---ps:布尔值false转化为数字0而"0"转化为数字也是0则结果为itrue
"0" == NaN;//false
"0" == 0 ;//true
"0" == "";//false
false == null;//false
false == undefined;//false
false == NaN;//false
false == 0 ;//true---ps:布尔值转化为数值型为0与右侧0相等则结果为true
false == "";//true---ps:布尔值false与空字符串转化为数值型都为0则结果为true
false == [];//true---ps:false转化为数值为0,空数组使用抽象操作ToPrimitive转化为空字符串,空字符串转换为数值型为0,则结果为true
false =={};//false
"" == null;//false
"" == undefined;//false
"" == NaN;//false
"" == 0;//true---ps:空字符串转化为数值型为0;右侧也是0则比较结果为true
"" == [];//true---ps:/true---ps:空数组使用抽象操作ToPrimitive转化为空字符串与左侧的空字符串相比结果为true
"" == {};//false
0 == null;//false
0 == undefined;//false
0 == NaN ;//false
0 == [];//true---ps:空数组使用抽象操作ToPrimitive转化为空字符串,空字符串转换为数值型为0,与左侧0相比结果为true
0 == {};//false
以上24种情况有17种比较好理解。比如我们都知道""和NaN不相等,"0"和0相等。
然而又7种进行了注释,因为它们属于假阳(false positive)情况,注意这里不存在假阴的情况。
3.3 极端情况
[]==![]//true
!运算符会使用ToBoolean规则,它进行布尔值的显示强制类型转换。所以[]==false,由于==在比较过程中会发生隐式强制类型转换,false转换为数值0,[]根据ToPrimitive规则转换为空字符串,空字符串转换为数值为0,则结果为true2==[2];//true
==右边的[2]会进行ToPrimitive强制类型转换为'2',再与左边的基本类型值2比较时'2'强制类型转换为数值2,左右相比结果为true""==[null]
由于[null]会进行ToPrimitive强制类型转换"",([null]在显示强制类型转换中也是转换为""),则与左边空字符串比较结果为true0=="\n";//true
""、"\n"(或者" "等其他空格组合)等空字符串被toNumber强制类型转换为0
42 == "43";//false
"foo" == 42;//false
"true" == true;//false
42 == "42";//true
"foo" == ["foo"];//true
3.4 完整性检查
以下是强制类型转换的7个坑请注意:
false == "0";//true
false == 0;//true
false === "";//true
false == [];//true
"" == 0;//true
"" == [];//true
0 == [];//true
3.5 安全运用隐式强制类型转换
我们要对==两边的值认真推敲,以下原则可以让我们有效避免出错:
- 如果两边的值中有true或者false,千万不要使用==
- 如果两边的值中有[]、""或者0,千万不要用==
这时最好使用===来避免不经意的强制类型转换。这两个原则可以让我们避开几乎所有强制类型转换的问题。
总的来说,==和===选择哪一个取决于在相等比较时是否允许发生强制类型转换
参考来源:
你不知道的js中卷--(4.5宽松相等于严格相等)