在 JavaScript 中,当对象需要被转换为原始值(字符串、数字或布尔值)时,会遵循特定的转换规则。这种转换通常发生在以下情况:
alert(obj)
输出对象obj1 + obj2
进行对象相加Number(obj)
或 +obj
将对象转换为数字String(obj)
或将对象作为属性键时JavaScript 对象到原始值的转换遵循以下步骤:
Symbol.toPrimitive
方法(ES6新增)Symbol.toPrimitive
,则检查 valueOf()
方法valueOf()
不存在或返回非原始值,则检查 toString()
方法alert(obj);
// 等价于
alert(String(obj));
转换顺序:
obj[Symbol.toPrimitive]('string')
(如果存在)obj.toString()
(如果存在)obj.valueOf()
(如果存在)Number(obj);
// 或数学运算(除了加法)
+obj;
转换顺序:
obj[Symbol.toPrimitive]('number')
(如果存在)obj.valueOf()
(如果存在)obj.toString()
(如果存在)加法运算 +
或 ==
比较时:
obj1 + obj2;
obj == primitive;
转换顺序与数字转换相同(默认 hint 会被视为 number)
ES6 引入了 Symbol.toPrimitive
方法,可以更精确地控制转换行为:
let user = {
name: "John",
money: 1000,
[Symbol.toPrimitive](hint) {
console.log(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.money;
}
};
// 转换演示:
alert(user); // hint: string -> {name: "John"}
console.log(+user); // hint: number -> 1000
console.log(user + 500); // hint: default -> 1500
如果没有 Symbol.toPrimitive
,JavaScript 会尝试 valueOf
和 toString
:
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"}
console.log(+user); // valueOf -> 1000
console.log(user + 500); // valueOf -> 1500
所有对象都从 Object.prototype
继承了默认的 toString
和 valueOf
方法:
toString
默认返回 "[object Object]"
valueOf
默认返回对象本身(不是原始值,因此通常会被忽略)对象到布尔值的转换很简单:所有对象(包括空对象)在布尔上下文中都是 true
。
if ({} || new Date()) {
// 这里的代码总会执行
}
场景 | 转换 hint | 方法调用顺序 |
---|---|---|
String(obj) |
"string" | Symbol.toPrimitive → toString → valueOf |
Number(obj) 或 +obj |
"number" | Symbol.toPrimitive → valueOf → toString |
obj + obj 或 obj == value |
"default" | Symbol.toPrimitive → valueOf → toString |
布尔上下文 | 无 | 总是 true |
理解这些转换规则对于避免 JavaScript 中的意外行为非常重要,特别是在进行对象操作和比较时。