在JavaScript中二元加法运算符”+”可以对两个数字或者字符串进行连接操作。
1+2=>3
"hello"+" "+"world" => "hello world"
"1"+"2"=>"12"
当两个操作数都是数字或者都是字符串时,得到的结果都是可预期的。但是对于其它情况来说,结果是如何呢?
var arr=[1];
var str="2";
var n=3;
console.log(str+arr);
console.log(n+arr);
console.log(n+null);
console.log(n+undefined);
var obj1={x:1};
console.log(arr+obj1);
var obj2={x:1};
console.log(obj1+obj2);
var b1=true;
var b2=true;
console.log(b1+b2);
var b3=new Boolean(false);
var b4=new Boolean(false);
console.log(b3+b4);
由于情况很多,这里就不进行逐一列举。对上面列举出来的情况,读者可以先自行思考。在文章的最后给出对应的答案。现在先讲一下”+”的处理过程,看看是否和读者思路一样。
对于类似上面列举的情况, “+”运算符要进行一些必要的转换,并且运算符的行为依赖于类型转换的结果。加号的转换规则优先于考虑字符串连接,如果其中一个操作数是字符串或者转换为字符串的对象,另外一个操作数将会转换为字符串,加法进行字符串连接操。如果两个操作数都不是类字符串,那么将进行算术加法运算。
从技术层面来讲,加法的操作表现行为如下:
1) 如果其中一个操作数是对象,则对象会遵循对象到原始值的转换规则转换为原始值。(可参考这篇文章:http://blog.csdn.net/u010533180/article/details/54427200)。日期对象通过toString()方法执行转换,其它对象则通过valueOf()方法执行转换。由于多数对象都不具备valueOf()方法,因此就会调用toString()方法来进行转换。
2)在进行对象到原始值的转换后,如果其中一个操作数是字符串的话,则另一个操作数也会转换为字符串,然后进行字符串的连接操作。
3)否则,两个操作数都将转换为数字(或者NaN),然后进行加法操作。
另外需要注意的是加号运算符和字符串和数字一起使用的时候,需要考虑加法的结合性对运算的影响。也就是运算结果是依赖运算符的运算顺序。
1+2+" hello world"=>"3 helo world"
1+(2+" hello world")=>"12 helo world"
下面来公布之前提到例子的答案:
var arr=[1];
var str="2";
var n=3;
console.log(str+arr); //21 数组转换为对应的字符串1
console.log(n+arr);//31 数组转换为对应的字符串1
console.log(n+null);// 3 null转换为0
console.log(n+undefined); //NaN undefined转换为NaN
var obj1={x:1};
console.log(arr+obj1); // 1[object Object] obj1转换为object
var obj2={x:1};
console.log(obj1+obj2); //[object Object][object Object]
var b1=true;
var b2=true;
console.log(b1+b2); //2 true 转换为1 进行计算
var b3=new Boolean(false);
var b4=new Boolean(false);
console.log(b3+b4); //0 b3 和b3得到原始值转换为0
运算符优先级表格:运算符按照优先级的不同从高到低排列。
优先级 | 运算类型 | 关联性 | 运算符 |
---|---|---|---|
19 | 圆括号 | n/a | ( … ) |
18 | 成员访问 | 从左到右 | … . … |
需计算的成员访问 | 从左到右 | … [ … ] | |
new (带参数列表) | n/a | new … ( … ) | |
17 | 函数调用 | 从左到右 | … ( … ) |
new (无参数列表) | 从右到左 | new … | |
16 | 后置递增(运算符在后) | n/a | … ++ |
后置递减(运算符在后) | n/a | … -- | |
15 | 逻辑非 | 从右到左 | ! … |
按位非 | 从右到左 | ~ … | |
一元加法 | 从右到左 | + … | |
一元减法 | 从右到左 | - … | |
前置递增 | 从右到左 | ++ … | |
前置递减 | 从右到左 | -- … | |
typeof | 从右到左 | typeof … | |
void | 从右到左 | void … | |
delete | 从右到左 | delete … | |
14 | 乘法 | 从左到右 | … * … |
除法 | 从左到右 | … / … | |
取模 | 从左到右 | … % … | |
13 | 加法 | 从左到右 | … + … |
减法 | 从左到右 | … - … | |
12 | 按位左移 | 从左到右 | … << … |
按位右移 | 从左到右 | … >> … | |
无符号右移 | 从左到右 | … >>> … | |
11 | 小于 | 从左到右 | … < … |
小于等于 | 从左到右 | … <= … | |
大于 | 从左到右 | … > … | |
大于等于 | 从左到右 | … >= … | |
in | 从左到右 | … in … | |
instanceof | 从左到右 | … instanceof … | |
10 | 等号 | 从左到右 | … == … |
非等号 | 从左到右 | … != … | |
全等号 | 从左到右 | … === … | |
非全等号 | 从左到右 | … !== … | |
9 | 按位与 | 从左到右 | … & … |
8 | 按位异或 | 从左到右 | … ^ … |
7 | 按位或 | 从左到右 | … | … |
6 | 逻辑与 | 从左到右 | … && … |
5 | 逻辑或 | 从左到右 | … || … |
4 | 条件运算符 | 从右到左 | … ? … : … |
3 | 赋值 | 从右到左 | … = … |
… += … | |||
… -= … | |||
… *= … | |||
… /= … | |||
… %= … | |||
… <<= … | |||
… >>= … | |||
… >>>= … | |||
… &= … | |||
… ^= … | |||
… |= … | |||
2 | yield | 从右到左 | yield … |
yield* | 从右到左 | yield* … | |
1 | 展开运算符 | n/a | ... … |
0 | 逗号 | 从左到右 | … , … |
文章参考来源:《JavaScript 权威指南》(原书第六版) 作者 弗兰纳根
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence