对象转原始类型toPrimitive

715 阅读2分钟

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 处理流程

  1. 如果input为Object类型时
    1. 是否存在外部定义的toPrimitive(exoticToPrim)? 这里指是否定义Symbol.toPrimitive
    2. 如果exoticToPrim定义了,则
      1. 如果没有指定需要装换的基本类型(preferredType),则让hint为"default"
      2. 如果装换的基本类型(preferredType)为string,则让hint为"string"
      3. 如果装换的基本类型(preferredType)为number,则让hint为"number"
      4. 调用exoticToPrim得到result
      5. 如果result不是Object类型则返回 result
      6. 如果还是Object类型则抛出异常TypeError
    3. 如果不存在 preferredType 则 把preferredType 当"number"处理
    4. 调用OrdinaryToPrimitive(input, preferredType) 的结果当返回值
  2. 非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处理流程

  1. 如果hint为String
    1. 则定义methodNames为 [toString, valueOf]
  2. 否则
    1. 则定义methodNames为 [valueOf, toString]
  3. 遍历 methodNames, 对于每个元素声明为name
    1. 获取O内的name定义为method
    2. 如果method为可调用的方法
      1. 调用method结果定义为result
      2. 如果result不为Object,则返回 result
  4. 最后抛出异常TypeError

参考出处: