从 ES 规范的角度讲解 +、Number 类型转换

分析下面代码的执行流程。html

var obj = {
  value: 3,
  valueOf() {
    return 4;
  },
  toString() {
    return '5'
  },
}
+obj // 4
''+obj // '4'
`${obj}` // '5'


复制代码

ToPrimitive 转换为原始值

Symbol.toPrimitive

原始值 primitive valueUndefined, Null, Boolean, Number, Symbol, String 之一,原始值是直接在语言实现的最低级别表示的数据。前端

当对象要被转换为数字或者字符串时,将会涉及到 ToPrimitive ( input [ , PreferredType ] ) 转换。程序员

  • PreferredType 表示对象将要转换的类型
    • PreferredType 未传递,则令 hintdefault
    • PreferredType 为 String,则 hintstring
    • PreferredType 为 Number,则 hintnumber

Symbol.toPrimitive 属性(用做函数值)的帮助下,一个对象可被转换为原始值。该函数被调用时,会被传递一个字符串参数 hint ,表示要转换到的原始值的预期类型。 hint 参数的取值是 "number"、"string" 和 "default" 中的任意一个。es6

经过Symbol.toPrimitive 咱们能够自定义在转换为目标原始值时的一些行为和返回值(返回值不能是非原始值,不然报错),若是没有这个属性,则会调用 valueOf 或者 toStringbash

var obj = {
  value: 3,
  valueOf() {
    return 4;
  },
  toString() {
    return '5'
  },
  [Symbol.toPrimitive](hint) {
    console.log('hint', hint)
    return 6
  }
}
复制代码

image

valueOftoString

若是没有定义 Symbol.toPrimitive,那么转换会经过 valueOftoStirng 处理以后返回。下面看看转换顺序和规则。函数

若是 hint 推断类型是 default(Date 对象除外) ,则认为它是 number,如今就只有 numberstring 两种类型。ui

  • hintstring,即要转换为字符串,则先用 toString处理,若是是原始值则返回,不然再用 valueOf 处理,若是返回是原始值则返回,不然会报 TypeError 错误;
  • hintnumber,则先用 valueOf 再用 toStringvalueOf 执行能返回原始值则直接返回不会执行 toString,若是 toString 返回的结果不是原始值则会报 TypeError 错误;

上面的规则应用于大多数场景。但对 Date 不适用,日期对象没有 hint,认为 hintstring 以下面的例子,'' + d,对通常对象,hintdefault,由前面知道 defaultnumber,对日期对象 hintstringthis

看下面三个例子es5

对象默认的 valueOf 返回本身。spa

var obj = {}

obj === obj.valueOf() // true
复制代码
var obj = {
  value: 3,
  valueOf() {
	console.log('valueof')
    return this;
  },
  toString() {
    return '5'
  },
}

+obj
// valueof 由于要转换位数字,因此执行了 valueOf,可是返回是对象,因此又执行 toString
// 5
复制代码
var d = new Date()

+d // 1571718957902 时间戳
''+d // "Tue Oct 22 2019 12:35:57 GMT+0800 (China Standard Time)" 字符串,涉及到先转换后拼接
`${d}` // "Tue Oct 22 2019 12:35:57 GMT+0800 (China Standard Time)" 字符串
复制代码

一元 + 运算符

+'5'
复制代码

规范

ToNumber(GetValue(expr))

归纳来讲就是转换时 hintnumber,对于原始值类型(Undefined, Null, Boolean, Number, Symbol, String),直接使用内部 ToNumber 转换成数字,下面是转换规则。对于对象,先使用 ToPrimitive 再使用 ToNumber

image

来几个例子。

+undefined // NaN
+null  // 0
+true // 1
+false // 0
+'6.11' // 6.11
+'6.1e15' // 6100000000000000
+'6.1e21' // 6.1e+21
+' 6 ' // 6
+' 6 7 8 ' // NaN
+'0xf' // 15
+'f' // NaN

var obj = {
  value: 3,
  valueOf() {
    return 4;
  },
  [Symbol.toPrimitive](hint) {
    console.log('hint', hint)
    return 6
  }
}
+obj
// hint number
// 6

var obj = {
  value: 3,
  valueOf() {
    return 4;
  },
}
+obj
// 4
复制代码

Number

Number 当作一个函数来使用。

规范

若是提供了 value,返回 ToNumber(value) 计算出的数字值(非 Number 对象),不然返回 +0。

从规范看出,Number+ 运算符少了 GetValueGetValue 有和没有对原始值是没有影响的,只对对象有影响。

Number(x)+x 的结果在大多数时候能够认为是彻底同样的,处理字符串时彻底能够互换。

参考


今天的文章就到这里,感谢阅读~

欢迎你们关注个人掘金和公众号,专一于提高前端程序员的核心竞争力

相关文章
相关标签/搜索