分析下面代码的执行流程。
var obj = {
value: 3,
valueOf() {
return 4;
},
toString() {
return '5'
},
}
+obj // 4
''+obj // '4'
`${obj}` // '5'
ToPrimitive 转换为原始值
Symbol.toPrimitive
原始值 primitive value 是 Undefined, Null, Boolean, Number, Symbol, String 之一,原始值是直接在语言实现的最低级别表示的数据。
当对象要被转换为数字或者字符串时,将会涉及到 ToPrimitive ( input [ , PreferredType ] ) 转换。
PreferredType表示对象将要转换的类型PreferredType未传递,则令hint为defaultPreferredType为 String,则hint为stringPreferredType为 Number,则hint为number
在 Symbol.toPrimitive 属性(用作函数值)的帮助下,一个对象可被转换为原始值。该函数被调用时,会被传递一个字符串参数 hint ,表示要转换到的原始值的预期类型。 hint 参数的取值是 "number"、"string" 和 "default" 中的任意一个。
通过Symbol.toPrimitive 我们可以自定义在转换为目标原始值时的一些行为和返回值(返回值不能是非原始值,否则报错),如果没有这个属性,则会调用 valueOf 或者 toString。
var obj = {
value: 3,
valueOf() {
return 4;
},
toString() {
return '5'
},
[Symbol.toPrimitive](hint) {
console.log('hint', hint)
return 6
}
}

valueOf 和 toString
如果没有定义 Symbol.toPrimitive,那么转换会通过 valueOf 、toStirng 处理之后返回。下面看看转换顺序和规则。
如果 hint 推断类型是 default(Date 对象除外) ,则认为它是 number,现在就只有 number 和 string 两种类型。
hint为string,即要转换为字符串,则先用toString处理,如果是原始值则返回,否则再用valueOf处理,如果返回是原始值则返回,否则会报TypeError错误;hint为number,则先用valueOf再用toString,valueOf执行能返回原始值则直接返回不会执行toString,如果toString返回的结果不是原始值则会报TypeError错误;
上面的规则应用于大多数场景。但对 Date 不适用,日期对象没有 hint,认为 hint 是 string。 如下面的例子,'' + d,对一般对象,hint 是 default,由前面知道 default 同 number,对日期对象 hint 是 string 。
看下面三个例子
对象默认的 valueOf 返回自己。
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))
概括来说就是转换时 hint 为 number,对于原始值类型(Undefined, Null, Boolean, Number, Symbol, String),直接使用内部 ToNumber 转换成数字,下面是转换规则。对于对象,先使用 ToPrimitive 再使用 ToNumber。

来几个例子。
+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 比 + 运算符少了 GetValue。GetValue 有和没有对原始值是没有影响的,只对对象有影响。
Number(x) 和 +x 的结果在大多数时候可以认为是完全一样的,处理字符串时完全可以互换。
参考
今天的文章就到这里,感谢阅读~
欢迎大家关注我的掘金和公众号,专注于提升前端程序员的核心竞争力
