等于(==)和不等于(!=)
这俩个操作做的工作,没必要多说,应该都知道,我们所需要注意的是,这俩种操作符都会就行隐式的类型转化,也可以说是比较的时候发现俩种类型不能比较,则会通过一些规则进行强制的类型转化,直到可以比较为止。
在转化的过程中会遵循如下规则:
- 如果任一操作数是布尔类型,则将其转化为数值再比较。false转化为0,true转化为1。
- 如果一个操作数是字符串,另一个操作数为数值,则尝试将字符串转化为数值,在就行比较。
- 如果一个操作数是对象,另一个不是,则调用对象的valueOf方法取得其原始值,再根据前面的规则比较。
而在比较的过程中,会遵循如下规则
- null和undefined相等
- null和undefined不能转化为其他类型的值再进行比较
- 如果有任一操作数是NaN,则相等操作符返回false,不相等操作符返回true
- 如果俩个操作数都是对象,则比较他们是不是同一个对象,如果俩个操作数都指向同一个对象,则相等操作符返回true。
上面的规则是《高级程序设计(第四版)》给出,仔细品一下上面的规则的话,其实所有的情况都已经涵盖。但是其中转化过程中的第三点说的比较模糊,所以做了一点实际的测试,来有一个更加清除的认识。
为什么说第三点有点模糊呢? 可以在浏览器控制台输入如下代码
({}).valueOf(); // {}
可以看到返回结果依然是一个对象,再根据前面俩条规则判断的话无法判断,那对于({}) == 1这样的表达式是如何判断的呢?下面继续来看。
测试一:
const obj = {
valueOf() {
console.log('执行了valueOf方法')
return 1;
},
toString() {
console.log('执行了toString方法')
return 2;
}
}
console.log(obj == 2); // 执行了valueOf方法 false
console.log(obj == 1); // 执行了valueOf方法 true
测试二:
const obj = {
valueOf() {
console.log('执行了valueOf方法')
return [];
},
toString() {
console.log('执行了toString方法')
return '2';
}
}
console.log(obj == 2); // 执行了valueOf方法 执行了toString方法 true
console.log(obj == 'a'); // 执行了valueOf方法 执行了toString方法 false
通过上面俩个简单的测试,可以看出,如果一个操作数是对象,另一个不是的话,会调用valueOf获取值,但是获取的值不为基础类型时,则会调用toString方法;如果是基础类型的话,根据前面俩条继续判断。
得出了这些结论后,我们接着看({}) == 1为什么等于false
- 首先
({})会调用valueOf方法,但是得到的仍然是一个对象 - 因此
({})调用toString方法,得到的是一个'[object Object]',然后就变成了'[object Object]' == 1 - 根据规则的第二条,我们再尝试将
'[object Object]'转化为数字 Number('[object Object]'),得到的是一个NaN,最后根据比较规则的第三条,得出false
其中字符串转化为数字的过程,我书中没有细说,但是我觉得是用Number()类似的规则做转化的,因为parseInt的话'1[object Object]' == 1这个表达式就为true了,当然这里如果有其他想法的话可以告知一二。
双等号在实际中用到的地方非常非常少,可以说几乎不用,因为存在很多的坑,但是其比较原理还是做一个巩固,当然文中有哪里说的是错的,也欢迎指正。