javascript中{} + {}的结果、js 加法运算

09月25日2015 阅读 javascript 看评论

当对象或者数组相加的时候,会产生有点意外的结果。

这篇文章主要是解释为什么会产生这种结果。

在JavaScript中加号操作的规则比较简单:只能对Number或者String相加,其他的值都会被转化成这两种类型中的一种。为了理解这种转化是怎么工作的,我们首先弄清一些事情,参考ECMA-262v5版本 9.1章节

快速复习下,在JavaScript中有两种值:primitives跟objects.原始类型的值有:
undefined null Boolean Number String.其他的所有值都是Object包括array跟function

1. 值的转换

加号运算符能执行三种转换:把值转化成primitive,数字跟字符串

1.1 通过ToPrimitive() 将值转换成原始类型

ToPrimitive(input, PreferredType?)
可选参数PreferredType是Number或者是String。返回值为任何原始值.如果PreferredType是Number,执行顺序如下:(参考:http://es5.github.io/#x9.1)

  1. 如果input为primitive,返回
  2. 否则,input为Object。调用 obj.valueOf()。如果结果是primitive,返回。
  3. 否则,调用obj.toString(). 如果结果是primitive,返回
  4. 否则,抛出TypeError

如果 PreferredType是String,步骤2跟3互换,如果PreferredType没有,Date实例被设置成String,其他都是Number

1.2通过ToNumber()把值转换成Number

直接看ECMA 9.3的表格http://es5.github.io/#x9.3

  • undefined NaN
  • null +0
  • boolean value true is converted to 1, false is converted to +0
  • number value no conversion necessary
  • string value parse the number in the string. For example, “324” is converted to 324
    要注意的是Object的转换,首先调用ToPrimitive(obj, Number)方法,然后调用ToNumber作为结果。
1.3通过ToString()把值转化成字符串

直接看ECMA 9.8的表格http://es5.github.io/#x9.8

  • undefined “undefined”
  • null “null”
  • boolean value either “true” or “false”
  • number value the number as a string, e.g. “1.765”
  • string value no conversion necessary
1.4 试试看

下边的代码可以看到转换过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
varobj={
valueOf:function(){
console.log("valueOf");
return{};// not a primitive
},
toString:function(){
console.log("toString");
return{};// not a primitive
}
}
Number(obj)//把obj转换成Number
//输出如下:
//valueOf 返回的不是原始类型,继续往下走
//toString 返回的不是原始类型,继续往下走
//TypeError: Cannot convert object to primitive value 抛出错误

2. 相加

value1 + value2
对此表达式求值时,遵循一下步骤(http://es5.github.io/#x11.6.1):

1.转换操作符两边的值为原始值

1
2
3
`prim1:=ToPrimitive(value1)`
`prim2:=ToPrimitive(value2)`

第二个参数PreferredType被忽略,所以对非Date类型都是Number,Date为String

2.如果prim1或者prim2有一个是String,把这俩值都转化成String,返回相连的结果。
3.否则,把prim1跟prim2都转化成Number返回相加后的结果

2.1 期望的结果

当你对两个数组相加的时候,结果跟预期的一样:
[] + [] // ''
转化[]到primitive的时候,首先尝试valueOf(),返回数组本身(this):

1
2
3
4
vararr=[];
arr.valueOf()===arr
//true

因为结果不是primitive,继续调用toString(),返回空字符串(这个是primitive类型)。因此[] + []的结果是两个空字符串相连接,还是空字符串

数组跟对象相加,也能得到期望的结果:

1
2
3
[]+{}
//'[object Object]'

解释:将一个空对象转化为String,有以下结果

1
2
3
String({})
//'[object Object]'

所以是空字符串””跟”[object Object]”的结合

更多例子:

1
2
3
4
5
6
7
5+newNumber(7)
12
6+{valueOf:function(){return2}}
8
"abc"+{toString:function()