如何让 (a == 1 && a == 2 && a == 3) 返回 true?

161 阅读2分钟

(仅为自己学习)

前两天看到了一道很有趣的题目,题目大意为:JS 环境下,如何让 a == 1 && a == 2 && a == 3 这个表达式返回 true

这道题目乍看之下似乎不太可能,因为在正常情况下,一个变量的值如果没有手动修改,在一个表达式中是不会变化的。当时我也冥思苦想很久,甚至一度怀疑这道题目的答案就是 不能。直到最近开始刷掘金发现了这个题的答案,奇奇怪怪的知识又增加了。

其中常见的答案是

const a = {  
  i1,  
  toStringfunction () {  
    return a.i++;  
  }  
}  
  
if (a == 1 && a == 2 && a == 3) {  
  console.log('Hello World!');  
}

原来这道题目的考点原来是 JS 获取一个变量所需要做的操作以及其中一些细节。在 JS 中有 ===== 两种方式来判断两个变量是否相等。对 JS 稍有了解的人都知道,=== 是严格相等,不仅需要两个变量的值相同,还需要类型也相同,而 == 则是宽松下的相等,只需要值相同就能够判断相等,宽松相等是严格相等的子集。

null == undefined // true  
null === undefined // false  
  
1 == '1' // true  
1 === '1' // false

大家可以自行补一下JS 特有的,变量宽松相等判断的各种情况。大致可以得到三种情况:

  1. 操作数 A 类型为 String,并且调用 +A 的结果与 B 严格相等
  2. 操作数 A 类型为 Boolean,并且调用 +A 的结果与 B 严格相等
  3. 操作数 A 类型为 Object,并且调用 toString 或者 ValueOf 返回的结果与 B 严格相等

所以上面的答案就是新建了一个对象 a ,并有 toString 方法,当 JS 引擎每次读取 a 的值的时候,发现需要进行宽松判断一个对象和一个数字之间的结果,对于对象就会执行这里的 toString 方法,在这个方法内部,我们每次增加另一个变量的值并返回,就能够在这条表达式中使得 a 的结果有不同的值。

同理,换一种写法,aObject ,使用 valueOf 也是可以完成目标的:

const a = {  
  i1,  
  valueOf () {  
    return this.i++;  
  }  
}  
  
if (a == 1 && a == 2 && a == 3) {  
  console.log('Hello World!');  
}

大家都可以尝试哈。