之前在社区看到这道题时,我第一反应就是如果我在代码库中看到这样的代码我肯定是很绝望的!!!言归正传,看到这题的时候,我首先想到的是(a == 1 && a == 2 && a==3)
为true的情况(宽松匹配),我们先了解这种情况,然后在解决扩展问题。
首先,在JS中,宽松匹配 ==
会先将左右两两边的值转化成相同的原始类型,然后再去比较他们是否相等。在转化之后(==
一边或两边都需要转化),最后的相等匹配会像 ===
符号一样去执行判断。
那么,我们考虑到的应该是a应该是什么类型,在a==
**的时候发生了什么?再去考虑a应该等于什么。
- 如果a是一个对象Object,那在执行a
==
的时候首先会去先执行valueOf
方法,如果没有valueOf
方法,就会去执行toString
方法。(toString
方法与valueOf
类似,这里不再重复)
const a = { value: 0 }
a.valueOf = function () {
return this.value += 1
}
console.log( a == 1 && a == 2 && a == 3 );
- 如果a是一个数组Array,在数组转换成字符串的时候,数组
toString
会隐含调用join()
方法
const a = [1, 2, 3];
a.join = a.shift;
console.log( a == 1 && a == 2 && a ==3 );
那么,(a === 1 && a === 2 && a === 3)
的值也能是true吗?
答案当然是肯定的!
但是,严格相等并没有转化的过程,所以我们需要通过一些方式去调用一个函数,并在这个函数中做我们想做的事情。但是执行函数往往需要在函数名字后引入 () ,并且由于这里不是宽松相等 ==
, valueOf
将不会被JS引擎调用。幸好Object提供了一个Property
函数, 特别是getter
描述符, 带来了解决这个问题的办法。
var value = 0; //window.value
Object.defineProperty(window, "a", {
get: function () {
return this.value += 1
}
})
console.log( a === 1 && a === 2 && a === 3 );
上面代码中,我们在window对象上定义了一个具有getter
的 a 属性, 通过get
属性, 我们可以调用一个函数并且不用在函数名后添加 (),所以 a 可以在代码中直接被访问到(全局变量), 因此也可以直接获得a的值。如果我们在其他对象上定义了属性 a 而不是window的话,例如object1, 我们就需要改变题目为 object1.a === 1 && object1.a === 2 && object1.a === 3
了。
扩展:字符编码实现相同效果
var aᅠ = 1;
var a = 2;
var ᅠa = 3;
console.log(aᅠ === 1 && a === 2 && ᅠa=== 3 );