ECMAScript7规范中的ToPrimitive抽象操作
引用类型转化为原始类型最核心的其实就是这个内部抽象方法,这一块理解了,后面在做题目巩固的时候思路就很清晰了
1、Symbol --- @@toPrimitive
Symbol有很多有名的符号,
@@toPrimitive,也就是Symbol.toPrimitive,这是定义在Symbol对象上的一个属性。
2、ToPrimitive(input [, PreferredType])
该抽象操作接受一个参数input和一个可选的参数PreferredType。该抽象操作的目的是把参数input转化为非对象数据类型,也就是原始数据类型。如果input可以同时转化为多个原始数据,那么会优先参考PreferredType的值。转化过程参照下表:
参数input的数据类型 | 结果 |
|---|---|
| Undefined | 返回input自身 |
| Null | 返回input自身 |
| Boolean | 返回input自身 |
| Number | 返回input自身 |
| String | 返回input自身 |
| Symbol | 返回input自身 |
| Object | 执行下面的步骤 |
如果input的数据类型是对象,执行下述步骤: |
- 如果没有传入
PreferredType参数,让hint等于"default"; - 如果
PreferredType是hint String,让hint等于"string"; - 如果
PreferredType是hint Number,让hint等于"number"; - 让
exoticToPrim等于GetMethod(input, @@toPrimitive),大概语义就是获取参数input的@@toPrimitive方法; - 如果
exoticToPrim不是Undefined,那么:- 让
result等于Call(exoticToPrim, input, « hint »),大概语义就是执行exoticToPrim(hint); - 如果
result是原始数据类型,返回result; - 抛出类型错误的异常;
- 让
- 如果
hint是"default",让hint等于"number"; - 返回
OrdinaryToPrimitive(input, hint)抽象操作的结果。
总结:
- 判断PreferredType,
只要传的值不是string,最后统一算作number` @@toPrimitive方法优先级高于OrdinaryToPrimitive(input, hint),如果前者能返回原始类型那就不需要执行后面的操作了- 如果
@@toPrimitive方法不存在或者存在返回的值不是原始类型,继续执行OrdinaryToPrimitive(input, hint)操作
疑问:hint是什么?怎么传的?(继续往下看)
3、OrdinaryToPrimitive(O, hint)
O的数据类型是对象,hint的数据类型是字符串,并且hint的值要么是"string",要么是"number"。该抽象操作的步骤如下:
- 如果
hint是"string",让methodNames等于« "toString", "valueOf" »; - 如果
hint是"number",让methodNames等于« "valueOf", "toString" »; - 按顺序迭代列表
methodNames,对于每一个迭代值name:- 让
method等于Get(O, name),大概语义就是获取对象O的name值对应的属性; - 如果
method可以调用,那么:- 让
method等于Call(method, O),大概语义就是执行method(); - 如果
result的类型不是对象,返回result;
- 让
- 让
- 抛出类型错误的异常。 由上述操作步骤可知:
- 通过
ToPrimitive的步骤6可知,当没有提供可选参数PreferredType的时候,hint会默认为"number"; - 通过
ToPrimitive的步骤4可知,可以通过定义@@toPrimitive方法来覆盖默认行为,比如规范中定义的Date日期对象和Symbol符号对象都在原型上定义了@@toPrimitive方法。
总结:
- 如果
hint是"string",先执行toString,如果没有返回原始值继续执行valueOf方法 - 如果
hint是"number",先执行valueOf,如果没有返回原始值继续执行toString方法
4、hint
hint可以理解为根据场景自动判断
更倾向于转换成string还是number,如果无法判断倾向于转化哪一个,就是default。下面我们来模拟一下转化过程中获取hint值的过程
const obj = {
[Symbol.toPrimitive](hint) {
console.log(`hint: ${hint}`);
}
};
const arr = []
arr[obj]
// hint: string
const obj = {
[Symbol.toPrimitive](hint) {
console.log(`hint: ${hint}`);
}
};
-obj
// hint: number
var a = [1, 2, 3]
a[Symbol.toPrimitive] = function (hint) {
console.log(`hint: ${hint}`);
}
'' + a
// hint: default
5、总结:
1、引用类型转化的时候,先确定是否有@@toPrimitive方法,优先级高于OrdinaryToPrimitive(input, hint),如果前者能返回原始类型那就不需要执行后面的操作了
2、判断hint值
3、如果hint是"string",先执行toString,如果没有返回原始值继续执行valueOf方法
4、如果hint是"number",先执行valueOf,如果没有返回原始值继续执行toString方法
还有最后一篇文章,结合大量的题目映正前两节所掌握的内容,会更加有感觉