[ECMA-262]-深入探究隐式类型转换
JavaScript是弱类型的语言,在很多情况下会涉及到隐式类型转换,这种类型转换在JavaScript引擎内部进行。通过参考ECMA-262,我们对几种常见的隐式类型转换在引擎内部的执行逻辑进行分析。
ToPrimitive( input [ , PreferredType ] )
这个抽象操作有一个参数input(表示需要进行类型转换的数据),还有一个可选参数PreferredType(表示期望转换成的类型)。
如果input是原始值,那么就直接返回输入本身。
如果input是对象,那么进行如下的操作:
如果没有参数PreferredType,那么设置hint为“default”。
如果参数PreferredType是String或者Number,那么分别设置hint为“String”或者“Number”。
判断input有没有@@toPrimitive方法(是一个用于将对象转成原始值的方法)
- 如果有,则调用该方法,并返回result,如果result不是对象类型,那么result就是最终的转换结果,结束,否则抛出TypeError异常。
- 如果没有,继续步骤4。
如果hint是”default“,那么设置hint为”Number“。
调用OrdinaryToPrimitive方法,返回OrdinaryToPrimitive(input, hint)的值。
- 确保hint是一个字符串,且只能是“string”或“number”。
- 如果hint是”String“,设置methodNames 为 < “toString”, “valueOf” >,否则,设置methodNames 为 < “valueOf”, “toString” >。
- 如果methodNames中的方法是可以调用的方法,那么就依次按顺序调用,如果返回值不是Object,那么OrdinaryToPrimitive直接返回这个返回值,结束。否则继续调用下一个方法。
- 如果遍历methodNames中的的所有方法,都没有符合条件的返回值,那么抛出TypeError异常。
注意,如果没有设置PreferredType 的值,那么会默认其为Number。但是,对象可能会通过重写@@toPrimitive方法来改变这一默认行为。Date对象和Symbol对象就改变了这一默认操作。Date对象在没有设置PreferredType的情况下,默认其为String。
ToNumber ( argument )
该操作遵循的规则如下表所示:
| Argument Type | Result |
|---|---|
| Undefined | Return NaN. |
| Null | Return +0. |
| Boolean | If argument is true, return 1. If argument is false, return +0. |
| Number | Return argument (no conversion). |
| String | See grammar and conversion algorithm below. |
| Symbol | Throw a TypeError exception. |
| Object | Apply the following steps: 1. Let primValue be ToPrimitive(argument, hint Number). 2. Return ToNumber(primValue). |
其中对String和Object的转换需要详细分析。
ToNumber(String)
如果字符串不能解释为字符串数字字面量(StringNumericLiteral),那么结果为NaN。
StringNumericLiteral的定义如下:
StrWhiteSpace(opt)
StrWhiteSpace(opt) StrNumericLiteral StrWhiteSpace(opt)
StrWhiteSpace的定义:StrWhiteSpaceChar StrWhiteSpace(opt)
- StrWhiteSpaceChar的定义:
- WhiteSpace(
<LF> <CR> <LS> <PS>) - LineTerminator(
<TAB> <VT> <FF> <SP> <NBSP> <ZWNBSP> <USP>)
- WhiteSpace(
- StrWhiteSpaceChar的定义:
StrNumericLiteral的定义:
StrDecimalLiteral
定义:
StrUnsignedDecimalLiteral
定义:
Infinity
DecimalDigits . DecimalDigits(opt) ExponentPart(opt)
. DecimalDigits ExponentPart(opt)
DecimalDigits ExponentPart(opt)+StrUnsignedDecimalLiteral
-StrUnsignedDecimalLiteral
BinaryIntegerLiteral
OctalIntegerLiteral
HexIntegerLiteral
通过以上的定义我们可以知道,字符串数字字面量(StringNumericLiteral)和数字字面量(NumericLiteral)有以下几点不同之处:
StringNumericLiteral的开头和结尾可能包含着空白和行终止符。
例如:
1
2Number('123\n');//123
Number(' 123 ');//123StringNumericLiteral的十进制数前面可以有任意多个0,而NumericLiteral有前导零时,代表八进制数。
例如,表达式
Number("012") === 012的结果为false,因为前者等于12,而后者是八进制数,转换成十进制后是10.StringNumericLiteral可以添加
+``-来声明符号位。StringNumericLiteral在空或只包含空白字符的时候,转换结果为+0。
例如:
1
2
3
4Number('\n'); // 0 行分隔符系列
Number('');// 0,空白字符系列
//注意’\0‘并不包含在StrWhiteSpace的定义中
Number('\0');//NaN
ToNumber(Object)
这里要用到之前分析的ToPrimitive,先把对象类型值转换为原始值,然后在调用ToNumber函数
1 | primValue = ToPrimitive(argument, hint Number); |
ToString( argument )
该操作遵循的规则如下表所示:
| Argument Type | Result |
|---|---|
| Undefined | ‘undefined’ |
| Null | ‘null’ |
| Boolean | ‘true’ or ‘false’ |
| Number | NumberToString(argument) |
| String | argument |
| Symbol | Throw a TypeError exception |
| Object | Apply the following steps: 1. Let primValue be ToPrimitive(argument, hint String). 2. Return ToString(primValue). |
ToBoolean
| Argument Type | Result |
|---|---|
| Undefined | Return false. |
| Null | Return false. |
| Boolean | Return argument. |
| Number | If argument is +0, -0, or NaN, return false; otherwise return true. |
| String | If argument is the empty String (its length is zero), return false; otherwise return true. |
| Symbol | Return true. |
| BigInt | If argument is 0n, return false; otherwise return true. |
| Object | Return true. |