前言
这个问题,有取巧的解法,但是当被问到这道题,面试官真正想考察的是你对于隐式转换这个知识点的理解,还有你能否考虑到数据劫持
取巧解法
let i=1;
with({
get a() {
return i++;
}
}){
if(a==1 && a==2 && a==3) {
console.log('哈哈哈,成功了');
}
}
隐式转换
js中通过==
对两个值进行比较的时候,会做如下操作:
- 将两个被比较的值转换为同一类型
- 转换后(等式的一边或者两边都可能会被转换),在进行值的比较
最终的比较方式等同于全等操作符===
的比较方式, 相等操作符满足交换律。
相等操作符对于不同类型的值,进行的比较如下图所示:
从表中可以得到几点信息为了让(a == 1)
,a
只有这几种:
- a类型为
String
,并且可转换为数字1('1' == 1 => true)
- a类型为
Boolean
,并且可转换为数字1 (true == 1 => true)
- a类型为
Object
,通过转换机制后,可转换为数字1 (请看下文)
对象转原始类型的"转换机制"
规则1和2没有什么特殊的地方,我们来看看3:
对象转原始类型,会调用内置的[ToPrimitive]
函数,逻辑大致如下:
- 如果有
Symbol.toPrimitive
方法,优先调用再返回,否则进行2。 - 调用
valueOf
,如果可以转换为原始类型,则返回,否则进行3。 - 调用
toString
,如果可以转换为原始类型,则返回,否则进行4。 - 如果都没有返回原始类型,会报错。
解法:
const a = {
i: 1,
[Symbol.toPrimitive]() {
return this.i++
}
}
// 每次进行a == xxx时都会先经过Symbol.toPrimitive函数,自然也就可以实现a依次递增的效果
if (a == 1 && a == 2 && a == 3) {
console.log('哈哈哈,成功了')
}
当然也可以利用valueOf
和toString
const a = {
i: 1,
valueOf() {
return this.i++
}
}
// 每次进行a == xxx时都会先经过valueOf/toString函数,自然也就可以实现a依次递增的效果
if (a == 1 && a == 2 && a == 3) {
console.log('哈哈哈,成功了')
}
数据劫持
Object.defineProperty
, 通过劫持window
对象,每次读取a
属性时,都给_a
增加1.
Object.defineProperty(window, 'a', {
get() {
return _a++;
}
})
那能想到defineProperty
自然会想到proxy
let a = new Proxy({
i:1}, {
get(target){
console.log(target);
return () => target.i++
}
})
三种方式,你学会了吗?