Skip to content

Day04 如何让 (a == 1 && a == 2 && a == 3) 的值为true?

ScarSu edited this page Sep 29, 2020 · 1 revision

先从ECMA标准看看:==比较的原理

ES文档 - equality-comparison

x == y的比较(其中x和y是值)会返回true或false。 比较执行过程如下:

  1. x和y类型相同:返回x === y

  2. 一个是null,一个undefined:返回true

  3. 一个是number一个是string:返回number == ToNumber(string)

  4. 一个是bigint,一个是string: 返回StringToBigInt(string) 结果是 NaN ?false :bigint == StringToBigInt(string)

  5. 如果类型是Boolean, 执行转换 **ToNumber(boolean)**后再比较 .

  6. 一个是String, Number, BigInt, Symbol,另一个是Object, 对object执行转换**ToPrimitive(object)**后再比较

  7. 一个是Number, 另一个是BigInt:

    1. 如果x或y等于 NaN, +∞, 或-∞, 返回false.
    2. 比较数值,相等返回true
  8. 其他任何情况都返回false.

要满足a == 1 && a == 2 && a == 3,假设a是原始类型:

  • number,找不到满足条件的number,不成立

  • bigint,根据上面原理的第7条,会直接比较值,找不到满足条件的bigint,不成立

  • string,根据上面原理的第3条,会转换成number,找不到满足条件的string,不成立

  • boolean,根据上面原理的第5条,会被转成number再比较,结果是0或1,不成立

  • null,根据上面原理的第8条,结果是false,不成立

  • undefined,同上

  • symbol,同上

所以a不可能是原始类型,只能是object类型。

思路1:object转换为原始值的几个方法

根据==比较原理的第6条,对object执行ToPrimitive(object),转换为原始值后再比较

ECMA种定义的ToPrimitive(object)的执行顺序:

  1. 如果obj[Symbol.toPrimitive]接口存在,则调用该接口
  2. 如果object上的自身方法valueOf存在,则调用valueOf
  3. 如果object上的自身方法toString存在,则调用toString
  4. 上溯对象原型链查找valueOf和toString方法,根据不同原型,valueOf和toString优先级不同
    • date/string类型 toString>valueOf
    • number类型 valueOf>toString
    • ...

因此可以通过更改对象的获取原始值的方法,达到满足a == 1 && a == 2 && a == 3的目的

  • Symbol.toPrimitive
  • valueOf
  • toString
let a = {
    [Symbol.toPrimitive]: (function(hint) {
            let i = 1;
            //闭包的特性之一:i 不会被回收
            return function() {
                return i++;
            }
    })()
}
console.log(a == 1 && a == 2 && a == 3); //true

let a = {
    valueOf: (function() {
        let i = 1;
        //闭包的特性之一:i 不会被回收
        return function() {
            return i++;
        }
    })()
}
console.log(a == 1 && a == 2 && a == 3); //true

思路2:对象属性劫持

  • Object.defineProperty
  • Proxy
let i = 1;
Object.defineProperty(window, 'a', {
    get: function() {
        return i++;
    }
});
console.log(a == 1 && a == 2 && a == 3); //true
let a = new Proxy({}, {
    i: 1,
    get: function () {
        return () => this.i++;
    }
});
console.log(a == 1 && a == 2 && a == 3); // true

思路3:with关键字

let i = 0;

with ({
    get a() {
        return ++i;
    }
}) {
    console.log(a == 1 && a == 2 && a == 3); //true
}