引题
此处暂停30秒,把答案记在心理。打开控制台看看结果吧。
没错,答案是:true
剖析一下,主要分为:
!逻辑运算符的优先级;{}与[]复杂数据在做运算比较时类型如何转换(隐式转换); 逻辑运算符这个比较简单,不多讲,可以参考运算符优先级-mdn
接下来就是js的隐式转换
首先,回想一下JS的类型都有什么:
原始值(primitives): undefined、null、Boolean、Number、String、Symbol、Bigint
对象值(objects): Object(包含数组、正则、对象、方法等)
ok, 这就是全部了,我们接下来看看到底发生了什么导致隐式转换如此不可捉摸。
ECMAScript规范
既然要搞懂,就得看去看ECMAScript规范里面==是如何操作的传送门。
重点看对象值,里面ToPrimitive ToPrimitive就是转换成原始值,然后我们再看下ToPrimitive里面操作:
简单解读一下,大概意思就是:
-
value为原始值,直接返回; -
不是原始值,调用该对象的
valueOf()方法,如果结果是原始值,返回原始值; -
调用
valueOf()不是原始值,调用此对象的toString()方法,如果结果为原始值,返回原始值; -
如果返回的不是原始值,抛出异常
TypeError
我们知道隐式转换规则了,那我们再来看 :[]==![] -
[]不是原始值,调用对象相关的valueOf()
2. 调用valuerOf()过后返回的还是 [],所以继续调用对象的toString()
- 调用
toString()过后返回'' - 逻辑运算符:
![] 的值是 false - 隐式转换过后:
'' == ![]==〉'' == false;
现在就是字符串和布尔相比较了,答案直接出来不是。
等等 。'' == false也没有是两个相等的值比较啊,这肯定也需要转换的。
MDN 非严格相等
看下mdn怎么说非严格相等-mdn传送门
引用mdn原话:相等操作符比较两个值是否相等,在比较前将两个被比较的值转换为相同类型。在转换后(等式的一边或两边都可能被转换),最终的比较方式等同于全等操作符 === 的比较方式。 相等操作符满足交换律。
String和Boolean 比较都会通过ToNumber转换过后再 === 比较。
整个隐式转换的过程就完成了。
小试牛刀
如何实现a==1&&a==2&&a==3为true?
方案1: 重写对象的valueOf()
const a = {
b: 0,
valueOf() {
return ++this.b;
}
}
if (a == 1 && a == 2 && a == 3) {
console.log(true);
}
方案2: 上面我们说过,对象值在进行比较时会先调用valueOf(),调用valueOf()不是原始值,调用此对象的toString()方法,所以我们也可以重写toString()
const a = {
b: 0,
toString() {
return ++this.b;
}
}
if (a == 1 && a == 2 && a == 3) {
console.log(true);
}
当然,还有其他的方案,这里不做探究。