这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情
前言
- 对象无法直接进行运算符的处理例如加减或直接显示(alert)
这里有个疑问,那为什么不行呢?
- 因为
obj1 + obj2的结果不能是另一个对象(其他的运算也会这样),而解决这个问题的方法就是转换成原始值。
转换规则
- 无需进行布尔值转换,因为在布尔上下文均为
true - 数字转换主要是在对象相间和数学函数时发生 (date1 - date2)
- 在输出对象时会有字符串转换(
alert(obj))
转换的三种变体(hint)
String
-
对象到字符串的转换,当我们对期望一个字符串的对象执行操作时,如 “alert”:
// 输出 alert(obj); // 将对象作为属性键 anotherObj[obj] = 123;
number
-
对象到数字的转换,例如当我们进行数学运算时:
// 显式转换 let num = Number(obj); // 数学运算(除了二元加法) let n = +obj; // 一元加法 let delta = date1 - date2; // 小于/大于的比较 let greater = user1 > user2;
default
- 当遇到类似 + 这种即可用于字符串(连接),也可用于数字(相加)。因此,这时就会依据“default”对齐进行转化;如果对象被用于与字符串、数字或 symbol 进行
==比较,这时到底应该进行哪种转换也不是很明确,因此使用"default"hint。
// 二元加法使用default hint
let total = obj1 + obj2;
// obj == number 使用default hint
if (user == 1) { ... };
为了进行转换,JavaScript 尝试查找并调用三个对象方法:
- 调用
obj[Symbol.toPrimitive](hint)—— 带有 symbol 键Symbol.toPrimitive(系统 symbol)的方法 - hint 是
"string"/"number"或"default"—— 尝试调用obj.toString()或obj.valueOf(),无论哪个存在。
Symbol.toPrimitive
例如,这里 user 对象实现了它:
let user = {
name: "John",
money: 1000,
[Symbol.toPrimitive](hint) {
alert(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.money;
}
};
// 转换演示:
alert(user); // hint: string -> {name: "John"}
alert(+user); // hint: number -> 1000
alert(user + 500); // hint: default -> 1500
- 通过上面的代码可知,就是判断hint是什么类型的,根据不同类型获取不同的原始值进行运算/处理
toString/vauleOf
-
有时会遇到没有
Symbol.toPrimitive的情况,JS会尝试寻找toString和valueOf方法 -
字符串会优先调用
toString方法,返回一个字符串"[object Object]"。如果没有Symbol.toPrimitive和valueOf,toString将处理所有原始转换。 -
其他类型优先调用
valueOf方法,返回对象自身。
下面看这段代码:
let user = {
name: "John"
};
alert(user); // [object Object]
alert(user.valueOf() === user); // true
所以,如果我们尝试将一个对象当做字符串来使用,例如在 alert 中,那么在默认情况下我们会看到 [object Object]。而默认的 valueOf 它返回对象本身,因此被忽略。
let user = {
name: "John",
money: 1000,
// 对于 hint="string"
toString() {
return `{name: "${this.name}"}`;
},
// 对于 hint="number" 或 "default"
valueOf() {
return this.money;
}
};
alert(user); // toString -> {name: "John"}
alert(+user); // valueOf -> 1000
alert(user + 500); // valueOf -> 1500
- 这段代码,我们可以看见执行的动作和前面使用
Symbol.toPrimitive的那个例子相同。只不过是根据不同类型分别调用这两个方法而已
进一步转换
- 有时我们将对象转换为原始值后,由于韩需要进一步计算这时需要原始值进一步转换
例如:
let obj = {
// toString 在没有其他方法的情况下处理所有转换
toString() {
return "2";
}
};
alert(obj * 2); // 4,对象被转换为原始值字符串 "2",之后它被乘法转换为数字 2。
- 乘法
obj * 2首先将对象转换为原始值(字符串 “2”)。 - 之后
"2" * 2变为2 * 2(字符串被转换为数字)。
二元加法在同样的情况下会将其连接成字符串,因为它更愿意接受字符串:
let obj = {
toString() {
return "2";
}
};
alert(obj + 2); // 22("2" + 2)被转换为原始值字符串 => 级联