首先来看一个有意思的面试题:
if(a == 3 && a == 4){//...
}
第一眼看到这个面试题我是拒绝的,这个等式根本不会成立,怎么会存在一个值既等于3并且还同时等于4呢?根本不可能。
但是在神奇的javascript中这个a是存在的。(对javascript要永远怀着一颗敬畏的心)
var i = 3;
var a = new Object();a.valueOf = function(){return i++;
}if(a == 3 && a == 4){console.log('等式成立了'); // 打印了
}
简单一看这个面试题有点故弄玄虚,谁会这样写代码?(我之前的想法)
但是这其中的知识点很有用,并且不是很好掌握。
其中的主要知识点就是宽松等于(==)涉及到的隐式类型转换。在日常开发中总会被提醒尽量不要使用宽松等于,在相等比较时尽可能的使用严格等于。但是为什么呢?其实通过上面的面试题就可以看出来,宽松等于太神奇了(晦涩难懂,莫名其妙,坑太多),当然这是对于不太了解宽松等于和类型转换的我来说的。接下来则会不自量力的尽力来解释一下类型转换和宽松等于。
首先对于 == 和 === 的区别常被解释为 == 并不检查数据类型,=== 检查数据类型。但是这个说法并不完全正确。相对正确的说法是 == 允许数据进行类型转换而 === 并不允许。第一种说法貌似是 === 做了更多的事情,但事实却不是这样的。
== 的几种情况分别是
-
两个值类型相同则比较值
1.1 NaN不等于自身
1.2 +0 等于 -0
1.3 两个对象指向同一个值则相等(不发生强制类型转换)
1.4 === 对于对象的比较同1.3
-
数字和字符之间的比较
-
Boolean和其他之间的比较
-
对象和非对象之间的比较
#数字和字符串之间的比较
一个String类型值和一个Number类型值之间比较则将String类型值按照 转换为Number表转为Number类型。
var a = '1';
var b = 1;console.log(a == b); // true 字符‘1’被转换成了数字1再作比较则相等
console.log(a === b); // false 并没有对字符'1'数据类型转换,数据类型不同则不相等
Boolean类型值和其他类型值之间的比较
一个Boolean类型值和一个其他类型值之间比较则首先将Boolean类型值转换为Number类型,true是1而false是0。
var a = true;console.log(a == 1); // true转换成了数字1再作比较则相等
Object和其他类型值之间的比较
一个Object类型值和一个其他类型值之间比较则将Object类型值按照如下步骤转化:
- 该对象如果有valueOf方法则调用该方法
- 若该valueOf的返回值是基本类型则用作比较
- 若valueOf的返回值不是基本类型则调用toString方法
- 若toString方法返回基本类型值则用作比较
- 若toString返回值不是基本类型则报错
var a = {valueOf: function(){return 1;},toString: function(){return 2;}
};console.log(a == 1); // true 调用a.valueOf获得返回值再比较
var a = {valueOf: function(){return '1';},toString: function(){return 2;}
};console.log(a == 1); // true
以上代码当对象a和数字1作比较调用valueOf方法获得字符串‘1’,这时变成了字符串’1’和数字1作比较,根据字符串和数字比较的规则,将字符串转换成数字等到数字1,然后两个数字1作比较得出相等。
当没有valueOf方法或者valueOf方法不返回基本类型值的时候则调用toString方法。
var a = {toString: function(){return 1}
};console.log(a == 1); // true
到这里开篇的那道面试题则不难理解了,宽松等于中对象和其他类型比较时涉及到了隐式强制类型转换,首先会调用valueOf方法,如果有必要还会调用toString方法来获取值来进行比较。
Null和Undefined之间的比较
在宽松等于比较中Null类型值只和自身还有Undefined类型值相等。
console.log(null == undefined); // trueconsole.log(null == false); // falseconsole.log(undefined == false); // falseconsole.log(null == ''); // falseconsole.log(undefined == ''); // falseconsole.log(null == 0); // falseconsole.log(null == ''); // false
几个宽松等于的坑
console.log(![] == []); // true
// 取反的优先级高于 ==,所以![]转为false,false转为0,[]调用valueOf得到[],所以调用toString得到‘’,然后''转为0,得到相等console.log(false == []); // true
//false转为 0,[]根据上面的步骤转为0,得到相等"0" == false; // true 注意"0"并不是假值但是这里却是相等的,因为 false 转为 0,变成了数字和字符比较,字符串"0"转为了数字0得到相等
宽松等于中值a与Boolean作比较并不是比较值a是否为真或假,而是值a与转换过后的Boolean值(0或1)作比较。
参考
你不知道的javascript(中卷)