Skip to content

让 (a == 1 && a == 2 && a == 3) 的值为true #45

Open
@TieMuZhen

Description

@TieMuZhen

前备知识

首先了解类型转换规则Proxy 与 Object.defineProperty区别,然后思考当对象相加obj1 + obj2,相减obj1 - obj2obj1 == 2或者使用alert(obj)打印时会发生什么?

在这种情况下,对象会被自动转换为原始值,然后执行操作。

注意: 所有的对象在布尔上下文(context)中均为 true。所以对于对象,不存在 boolean 转换,只有字符串和数值转换
JavaScript 尝试查找并调用三个对象方法:

  1. 调用obj[Symbol.toPrimitive](hint) —— 如果这个方法存在的话,
  2. 否则,如果 hint 是"string" —— 先尝试obj.toString() obj.valueOf(),无论哪个存在。
  3. 否则,如果 hint 是 "number" "default" —— 先尝试obj.valueOf() obj.toString(),无论哪个存在。

下面的例子展示了, Symbol.toPrimitive属性是如何干扰一个对象转换为原始值时输出的结果的

+前缀是转换为数字,即Number(),!!前缀是转换为布尔型

// 一个没有提供 Symbol.toPrimitive 属性的对象,参与运算时的输出结果
var obj1 = {};
console.log(+obj1);      // NaN
console.log(`${obj1}`);  // "[object Object]"
console.log(obj1 + ""); // "[object Object]"

// 接下面声明一个对象,手动赋予了 Symbol.toPrimitive 属性,再来查看输出结果
var obj2 = {
  [Symbol.toPrimitive](hint) {
    if (hint == "number") {
      return 10;
    }
    if (hint == "string") {
      return "hello";
    }
    return true;
  }
};
console.log(obj2);        // "hello" -- hint 参数值是 "string"
console.log(+obj2);      // 10      -- hint 参数值是 "number"
console.log(`${obj2}`);  // "hello" -- hint 参数值是 "string"
console.log(obj2 + ""); // "true"  -- hint 参数值是 "default"

toString/valueOf

方法toStringvalueOf方法出现的比 symbol早。

如果没有Symbol.toPrimitive,那么 JavaScript 将尝试找到它们,并且按照下面的顺序进行尝试:

  • 对于 “string” hint,toString -> valueOf
  • 其他情况,valueOf -> toString

默认情况下,普通对象具有 toString 和 valueOf 方法:

  • toString方法返回一个字符串"[object Object]"
  • valueOf方法返回对象自身
let user = {name: "John"};

alert(user);  // [object Object]
alert(user.valueOf() === user);   // true

通常我们希望有一个“全能”的来处理所有原始转换。在这种情况下,我们可以只实现toString

let user = {
  name: "John",

  toString() {
    return this.name;
  }
};

alert(user); // toString -> John
alert(user + 500); // toString -> John500

返回类型

没有限制 toString() 是否返回字符串,或 Symbol.toPrimitive 方法是否为 hint “number” 返回数字。唯一强制性的事情是:这些方法必须返回一个原始值,而不是对象

解题方法

隐式转换

let i = 1;
const a = {
    valueOf: function () {
        return i++;
    }
}

// 或者

const a = (function() {
    let i = 1;
    return {
        valueOf: function () {
            return i++;
        }
    }
})()

Symbol.toPrimitive

//部署 [Symbol.toPrimitive] / valueOf/ toString 皆可
//一次返回1,2,3 即可。
let a = {
    [Symbol.toPrimitive]: (function(hint) {
            let i = 1;
            //闭包的特性之一:i 不会被回收
            return function() {
                return i++;
            }
    })()
}

Proxy

let a = new Proxy({i: 1}, {
    get(target, key) { 
        return () => target.i++;
    }
});

Object.defineProperty

let val = 1;
Object.defineProperty(window, 'a', {
    get() {
        return val++;
    }
})

参考文章

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions