【笔记】js中加号(+)运算符的隐式转换
Dec 27, 2018笔记jsjs中加号(+)运算符的隐式转换
背景
1 | console.log(1 + 2); // 3 |
神奇不?加法运算符(+)通常只用作数字相加以及字符串拼接,但在处理特殊值时有着特殊的行为
1 隐式转换的原理
1.1 JavaScript引擎内部的ToPrimitive()方法
1 | ToPrimitive(input,PreferredType?) |
抽象操作ToPrimitive将其输入参数转换为非对象类型。如果一个对象能够转换为多种原始类型,那么它可以使用可选的参数preferredType来支持该类型。
其中,可选参数PreferredType可以是Number或者String。 它只代表了一个转换的偏好,转换结果不一定必须是这个参数所指的类型,但转换结果一定是一个原始值(基本类型)。如果PreferredType被标志为Number,则会进行下面的操作来转换input
- 第一步,如果input是个原始值,则直接返回它。
- 第二步,如果input是一个引用类型。则调用obj.valueOf()方法。如果返回值是一个原始值,则返回这个原始值。
- 第三步,调用obj.toString() 方法。 如果返回值是一个原始值,则返回这个原始值。
- 都不是,抛出TypeError异常
如果PreferredType为String,则转换操作的第二步和第三步的顺序会调换。
如果没有PreferredType这个参数,则PreferredType的值会按照这样的规则来自动设置:
- Date 类型的对象会被设置为 String
- 其它类型的值会被设置为 Number
1.2 JavaScript引擎内部的ToNumber()方法
ToNumber()方法将值转换为数字1
ToPrimitive(obj, Number)
参数 | 结果 |
---|---|
undefined | NaN |
null | +0 |
Boolean | true被转换为1,false转换为+0 |
Number | 无需转换 |
String | 由字符串解析为数字。例如,”123”被转换为123 |
如果输入的值是一个对象,则会首先会调用 ToPrimitive(obj, Number) 将该对象转换为原始值, 然后在调用 ToNumber() 将这个原始值转换为数字。
1.3 JavaScript引擎内部的ToString()将值转换为字符串
ToNumber()方法将原始值转换成字符串1
ToPrimitive(obj, String)
参数 | 结果 |
---|---|
undefined | “undefined” |
null | “null” |
Boolean | “true” 或者 “false” |
Number | 数字作为字符串。比如,”1.765” |
String | 无需转换 |
如果输入的值是一个对象,则会首先会调用 ToPrimitive(obj, String) 将该对象转换为原始值, 然后再调用 ToString() 将这个原始值转换为字符串。
2 加法运算符
1 | value1 + value2 |
在计算这个表达式时,内部的操作步骤是这样的
- 1.将操作值value1、value2转换为原始值prim1、prim2。(因为省略了preferredType,因此Date类型是值采用String,其他类型的值采用Number);
- 2.如果prim1或prim2中的任意一个为字符串,则将另外一个也转换成字符串,然后返回两个字符串连接操作后的结果。
- 3.prim1和prim2都不是字符串,则数字运算求和。
ES5及以上可参考下列计算结果:
value1/value2 | undefined | Boolean | Number | String | Function | Object | null | Array |
---|---|---|---|---|---|---|---|---|
undefined | Number | Number | Number | String | String | String | Number | String |
Boolean | Number | Number | Number | String | String | String | Number | String |
Number | Number | Number | Number | String | String | String | Number | String |
String | String | String | String | String | String | String | String | String |
Function | String | String | String | String | String | String | String | String |
Object | String | String | String | String | String | String | String | String |
null | Number | Number | Number | String | String | String | Number | String |
Array | String | String | String | String | String | String | String | String |
3 其他
3.1 {} + {}
返回值为NaN,原因是,JavaScript 把第一个 {} 解释成了一个空的代码块(code block)并忽略了它。 NaN 其实是表达式 +{} 计算的结果 (+ 加号以及第二个 {})。 你在这里看到的+加号并不是二元运算符「加法」,而是一个一元运算符,作用是将它后面的操作数转换成数字,和 Number() 函数完全一样。
为什么第一个 {} 会被解析成代码块(code block)呢? 因为整个输入被解析成了一个语句:如果左大括号出现在一条语句的开头,则这个左大括号会被解析成一个代码块的开始。 所以,你也可以通过强制把输入解析成一个表达式来修复这样的计算结果: (译注:我们期待它是个表达式,结果却被解析成了语句)
1 | console.log(({} + {})); // '[object Object][object Object]' |
Node.js 的 REPL 在解析类似的输入时,与 Firefox 和 Chrome(和Node.js 一样使用 V8 引擎) 的解析结果不同。1
2console.log(({} + {})); // '[object Object][object Object]'
console.log({} + []); // '[object Object]'
3.2 特殊行为
1 | +value |
- 某个运算数是
NaN
,那么结果为NaN
。 -Infinity
加-Infinity
,结果为-Infinity
。-Infinity
加-Infinity
,结果为NaN
。+0
加+0
,结果为+0
。-0
加+0
,结果为+0
。-0
加-0
,结果为-0
。
Author
My name is Micheal Wayne and this is my blog.
I am a front-end software engineer.
Contact: michealwayne@163.com