如何给a赋值,使a == 1 && a == 2 && a == 3返回true?

567 阅读3分钟

如何使a == 1 && a == 2 && a == 3返回true?

要使这个式子返回true,那么首先要a == 1,a == 2, a == 3都是true,我的第一反应是,在JS里面,相等操作符(==)在进行左右两边比较的时候,遇到左右两边的值为不同的类型时,会进行隐式类型转换(关于操作符隐式类型转换的问题可以看下这个JS个人学习(2)—操作符相关

简单来说,可以参考下图了解以下我们这里遇到的相等操作符(==)的隐式类型转换规则:

  • 两个值类型相同:
  1. String,Boolean: 正常比较两个值是否相同,相同为true,不同为false
  2. Undefined,Null: 返回true
  3. Number: 正常数值正常比较,有NaN均返回false,-0与+0返回true,-Infinity与Infinity返回false
  4. Object: 引用同一对象返回true,如let a = {}; let b = a; console.log(a == b); // true
  • 两个值类型不同:
  1. null与undefined返回true
  2. Number与String,String转Number进行比较
  3. 有Boolean类型的,先把Boolean转换成Number类型
  4. Object类型的,先获取其原始值,再进行比较
  5. undefined == 0返回false,null == 0返回false

详细一点的可以参考下图: image.png

从图中我们可以看出,一个变量a做不到同时等于1,2,3或者'1','2','3', 而且当a是Boolean类型时,只能转换成0或1,因此只有从a是Object类型时入手。

根据相等操作符(==)隐式类型转换的规则,在a是Object类型的时候,与Number类型的数值作比较,会先将对象转换成原始值,那么,对象的原始值是怎么获取的呢?

对象先通过Symbol.toPrimitive获取其原始值,如MDN上所说的, 它是一个内置的 Symbol 值,作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会先调用此函数。若Symbol.toPrimitive没有返回对应基本类型的值,则先调用对象的valueOf()方法,以取得一个可供操作的值,如果这个值是NaN,那么最后再调用toString()方法获取原始值。

综上所述,我们应该可以写出本题的至少两个答案:

答案一:

var a = {
    i: 1,
    valueOf: function () {
        return this.i++;
    }
}
console.log(a == 1 && a == 2 && a == 3); // true

答案二:

var a = {
    i: 1,
    toString: function () {
        return this.i++;
    }
}
console.log(a == 1 && a == 2 && a == 3); // true

当然,我上面说的是至少两个,那么还有一个答案三,就是重新写一下Symbol.toPrimitive:

答案三:

var a = {
    i: 1,
    [Symbol.toPrimitive]: function (hint) {
        return this.i++;
    }
}
console.log(a == 1 && a == 2 && a == 3); // true
// Symbol.toPrimitive在valueOf和toString()中的优先级是最高的

接下去可以再深入挖掘以下其他答案,其中我们可以利用数组转字符串的时候,也会用到toString()方法(因为数组先调用valueOf()返回的值是他本身),而toString()方法在转换数组的时候会join方法,因此:

答案四:

var a = [1, 2, 3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3); // true
// 数组的shift方法会移除数组的第一项,返回删除元素,并改变原数组。

除此之外,其实还有两种方法可以解决以上问题,但是我这次想记录的是关于隐式类型转换的问题,就不过多展开了。

不过,可以贴一下答案做个记录:

  • 定义a属性并重写它的getter方法:
const value = function* () {
  let i = 0;
  while(true) yield ++i;
}();
 
Object.defineProperty(this, 'a', {
  get() {
    return value.next().value;
  }
});
 
console.log(a == 1 && a == 2 && a == 3); // true
  • 利用Unicode的字符编码:同义字和隐形字符:
var a = 1;
var a‌ = 2;
var a‍ = 3;
console.log(a == 1 && a‌ == 2 && a‍ == 3);
/****
var a = 1;
var a\u200c = 2;
var a\u200d = 3;
console.log(a == 1 && a\u200c == 2 && a\u200d == 3);
****/