javascript 中的 toprimitive 是内部抽象操作,用于将对象转换为原始值(number、string 或 symbol)。它通过调用对象的 [symbol.toprimitive] 方法(如果存在),否则按提示类型(hint)调用 valueof() 和 tostring() 来决定最终结果。若未定义这些方法,则回退到默认行为。

如何根据 hint 选择 valueOf 还是 toString?
ToPrimitive 接收一个 hint 参数,可以是 "number"、"string" 或 "default"。对于大多数对象,"default" 等同于 "number";但对于 Date 对象,"default" 等同于 "string"。算法会优先尝试 valueOf(),如果返回的不是原始值,再尝试 toString()。反之,如果 hint 是 "string",则先尝试 toString(),再尝试 valueOf()。

const obj = {
valueOf() { return 42; },
toString() { return 'hello'; }
};
console.log(Number(obj)); // 42 (hint: number)
console.log(String(obj)); // 'hello' (hint: string)
console.log(obj + ''); // '42' (hint: default -> number)
上述代码展示了不同转换方式下,valueOf() 和 toString() 的调用顺序及结果。注意 obj + '' 触发的是 "default" hint,对于非 Date 对象,这被视为 "number",因此优先使用 valueOf() 的结果。
Symbol.toPrimitive 如何覆盖默认行为?
ES6 引入了 Symbol.toPrimitive,允许对象完全控制 ToPrimitive 的行为。当该符号属性存在时,ToPrimitive 直接调用它并传入 hint 参数,忽略 valueOf() 和 toString()。这使得开发者可以精确指定在不同上下文下的返回值。

const obj = {
[Symbol.toPrimitive](hint) {
if (hint === 'number') return 10;
if (hint === 'string') return 'test';
return null; // hint is 'default'
}
};
console.log(Number(obj)); // 10
console.log(String(obj)); // 'test'
console.log(obj + ''); // 'null'
在这个例子中,Symbol.toPrimitive 覆盖了默认的 valueOf/toString 逻辑。无论 hint 是什么,都按照自定义逻辑返回。特别是 obj + '' 得到 'null',因为 Symbol.toPrimitive('default') 返回了 null,然后被转换为字符串 'null'。
当 valueOf 和 toString 都返回对象时会发生什么?
如果 valueOf() 和 toString() 都返回对象(即都不是原始值),ToPrimitive 会抛出 TypeError。这是因为无法确定哪个方法应该作为最终结果的来源,且没有有效的原始值可返回。这是 ToPrimitive 的一个关键错误处理机制。
const badObj = {
valueOf() { return {}; },
toString() { return {}; }
};
try {
console.log(Number(badObj));
} catch (e) {
console.error(e.message); // Cannot convert object to primitive value
}
这段代码演示了当两个方法都失败时的情况。由于 valueOf() 返回对象,ToPrimitive 会继续尝试 toString()。如果 toString() 也返回对象,则抛出 TypeError。这确保了类型转换的确定性,避免歧义。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











