这里考核的其实是非严格相等运算符==
的原理,下面是摘自MDN的解释。
相等操作符比较两个值是否相等,在比较前将两个被比较的值转换为相同类型。在转换后(等式的一边或两边都可能被转换),最终的比较方式等同于全等操作符 === 的比较方式。
相等操作符对于不同类型的值,进行的比较如下图所示:
在上面的表格中,ToNumber(A) 尝试在比较前将参数 A 转换为数字,这与 +A(单目运算符+)的效果相同。ToPrimitive(A)通过尝试调用 A 的A.toString() 和 A.valueOf() 方法,将参数 A 转换为原始值(Primitive)。
结合这道题,B的类型是Number,那么在A==B
的比较中,可能存在两种情况的转换:
- 如果A类型是
String
和Boolean
,则通过ToNumber()
转换,它通过一元正号+
(也等同于Number()
函数)实现。这种实现是js语言内置的,无法重写。 - 如果A类型是
Object
,则通过toString()
和valueOf()
方法实现。这两个方法是A的方法,是可以重写的。
所以思路就有了,首先确定A是一个对象,然后通过重写A的toString()
和valueOf()
方法来达到目的。
所以代码是:
let a={
num:1,
toString(){
return a.num++
}
}
a==1&&a==2&&a==3 // true
或者:
let a={
num:1,
valueOf(){
return a.num++
}
}
a==1&&a==2&&a==3 // true
延伸
稍微修改上面的代码再来两个小测试,就能发现更多有意思的细节:
- 在转换的时候,会先调用
a.valueOf()
方法,然后依照返回的值(假定叫res
)做下一步的转换。 - 如果
res
是Object的类型,则接下来调用a.toString()
方法。 - 否则,不再继续调用
a.toString()
方法,而是依据上表,对res
做相应的转换,比如返回的是String
或者Boolean
,则进行ToNumber(res)
转换。
下面是测试代码:
// valueOf返回Object的情况
let a={
num:1,
toString(){
console.log('toString')
return a.num++
},
valueOf(){
console.log('valueOf')
return {num:2}
}
}
a==1
// valueOf
// toString
// true
// valueOf返回Boolean的情况
let a={
num:1,
toString(){
console.log('toString')
return a.num++
},
valueOf(){
console.log('valueOf')
return true
}
}
a==1
// valueOf
// true
// valueOf返回String的情况
let a={
num:1,
toString(){
console.log('toString')
return a.num++
},
valueOf(){
console.log('valueOf')
return '1'
}
}
a==1
// valueOf
// true
参考链接:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Equality_comparisons_and_sameness
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/57