Open
Description
前备知识
首先了解类型转换规则和Proxy 与 Object.defineProperty区别,然后思考当对象相加obj1 + obj2
,相减obj1 - obj2
,obj1 == 2
或者使用alert(obj)
打印时会发生什么?
在这种情况下,对象会被自动转换为原始值,然后执行操作。
注意: 所有的对象在布尔上下文(context)中均为 true。所以对于对象,不存在 boolean 转换,只有字符串和数值转换。
JavaScript 尝试查找并调用三个对象方法:
- 调用
obj[Symbol.toPrimitive](hint)
—— 如果这个方法存在的话, - 否则,如果 hint 是
"string"
—— 先尝试obj.toString()
后obj.valueOf()
,无论哪个存在。 - 否则,如果 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
方法toString
和valueOf
方法出现的比 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++;
}
})