JavaScript 对象转换到基本类型值时,会使用 ToPrimitive 算法
toPrimitive 是个抽象的操作 可通过使用Symbol.toPrimitive自定义
// 伪代码
/**
* toPrimitive
* @param {Object} input 如果非object类型则直接返回
* @param {string} [preferredType] 首选类型,需装换的基本类型
**/
function toPrimitive(input, preferredType) {
const exoticToPrim = input[Symbol.toPrimitive]
if (exoticToPrim) {
let hint
if (!preferredType) {
hint = 'default'
} else {
// 这里没有判断number 是因为
// Symbol.toPrimitive的hint 一定是 "number"、"string" 和 "default"中任意一个
hint = preferredType === 'string' ? 'string' : 'number'
}
const result = exoticToPrim(hint)
if (typeof result !== 'object') { return result }
throw new TypeError('error')
}
if (!preferredType) {
preferredType = 'number'
}
return OrdinaryToPrimitive(input, preferredType)
}
toPrimitive 处理流程
- 如果input为Object类型时
- 是否存在外部定义的
toPrimitive(exoticToPrim)? 这里指是否定义Symbol.toPrimitive - 如果
exoticToPrim定义了,则- 如果没有指定需要装换的基本类型(
preferredType),则让hint为"default" - 如果装换的基本类型(
preferredType)为string,则让hint为"string" - 如果装换的基本类型(
preferredType)为number,则让hint为"number" - 调用
exoticToPrim得到result - 如果
result不是Object类型则返回result - 如果还是Object类型则抛出异常
TypeError
- 如果没有指定需要装换的基本类型(
- 如果不存在
preferredType则 把preferredType当"number"处理 - 调用
OrdinaryToPrimitive(input, preferredType)的结果当返回值
- 是否存在外部定义的
- 非object则直接return input
// 伪代码
/**
* 默认toPrimitive
* @param {Object} O 对象
* @param {string} hint 转的类型
**/
function OrdinaryToPrimitive(O, hint) {
const methodNames = hint === 'string'
? ['toString', 'valueOf']
: ['valueOf', 'toString']
for (let i = 0; i < methodNames.length; i++) {
let name = methodNames[i]
if (typeof O[name] === 'function') {
const result = O[name]()
if (typeof result !== 'object') {
return result
}
}
}
throw new TypeError('error')
}
OrdinaryToPrimitive处理流程
- 如果
hint为String- 则定义
methodNames为 [toString, valueOf]
- 则定义
- 否则
- 则定义
methodNames为 [valueOf, toString]
- 则定义
- 遍历
methodNames, 对于每个元素声明为name- 获取
O内的name定义为method - 如果
method为可调用的方法- 调用
method结果定义为result - 如果
result不为Object,则返回result
- 调用
- 获取
- 最后抛出异常
TypeError
参考出处: