JS 加号运算符的运用
9月 18, 2021
一元运算符 #
语法: + Expression
说明: +
号运算符作为一元运算符时,Expression将进行 ToNumber 操作。
argument类型 | 返回值 |
---|---|
Undefined | return NaN |
Null | return +0 |
Boolean | true return 1; false return 0; |
Number | return value |
String | 若字符串为纯数字时返回转换后的数字;非纯数字返回NaN |
Symbol | 抛出 TypeError 异常 |
Object | 进行以下步骤:1.先进行ToPrimitive(argument, hint Number)得到rs; 2.然后返回 ToNumber(rs)的结果。 |
示例:
// Undefined
+ undefined; // => NaN
// Null
+ null; // => 0
// Boolean
+ true; // => 1
+ false; // => 0
// String
+ '1'; // => 1
+ '-1'; // => -1
+ 'a1'; // => NaN
// Object
+ {}; // => NaN
+ { valueOf: function () { return 0 } }; // => 0
二元运算符 #
+
作为二元运算符时简单概括为下面4个步骤:
值进行 GetValue 操作。
值进行 ToPrimitive 操作,
若一方为 String 类型,2个值都进行 ToString 转换,最后进行字符串连接操作。
若都不是 String 类型,2个值都进行 ToNumber 转换,最后进行算数加法运算。
其中方法 ToPrimitive
语法为:
ToPrimitive ( input [, PreferredType] )
input
表示传入的值。PreferredType
是可选值,表示需要被转换的类型。+
为一元运算符时,此值为“number”;而作为二元运算符时,未传递此值,以默认的“default”代替。
官方文档 ecma262 的计算步骤如下图所示:
其中 OrdinaryToPrimitive
如下:
简单解释一下,若 input 类型为原始值(如:Undefined、Null、Boolean、Number、String、Symbol),直接返回 input 的本身;若 input 类型为object(如:Array、Object、Date),将根据第 2 个参数 PreferredType 的值进行以下操作:
- 调用
obj[Symbol.toPrimitive](hint)
:带有 symbol 键Symbol.toPrimitive
(系统 symbol)的方法,如果这个方法存在的话。 - 否则,如果 PreferredType 是
"string"
:调用toString
方法,如果它不存在或结果是 object,则调用valueOf
方法。 - 否则,如果 PreferredType 是
"number"
或"default"
:调用valueOf
方法,如果它不存在或结果是 object,则调用toString
方法。
因此,对于字符串转换,会优先调用 toString
,对于数学运算,会优先调用 valueOf
方法,“default” 默认为 number,例外的情况是 Date 类型,Date类型内部重写了@@toPrimitive()方法,将“default”设置为“string”,而其他内置的对象都将“default”设置为“number”。
String + String #
进行字符串连接操作。
'a' + 'b'; // => 'ab'
'1' + '2'; // => '12'
Number + Number #
进行算数的加法操作。
1 + 2; // => 3
Number + String or String + Number #
Number类型的值先进行 ToString()
转换,随后再与另一个 String 类型的值进行拼接操作。
1 + '0'; // => '10'
1 + '0a'; // => '10a'
'1' + 2; // => 12
Array + String #
Array类型进行 ToPrimitive()
转换时,先执行 valueOf()
,因其返回一个 object 类型的值,所以又执行了 toString()
方法。
var tmpList = ['a', 'b', 'c'];
tmpList.valueOf(); // => ["a", "b", "c"] 输出自身
tmpList.toString(); // a,b,c
// 1.Array + String
tmpList + 'd'; // => a,b,cd
// 2.重写Array的valueOf()方法,使其返回一个非object
Array.prototype.valueOf = function (e) {
return this.join('-');
}
tmpList + 'd'; // => a-b-cd
Date + String #
Date 类型重写了 ToPrimitive()
,所以先调用 toString()
,若返回值为 object,再调用 valueOf()
。
var dt = new Date();
dt.valueOf(); // => 1503501745901
dt.toString(); // Wed Aug 23 2017 23:22:25 GMT+0800 (中国标准时间)
// 1.Date + String : dt直接使用了dt.toString()方法
dt + 'd'; // => Wed Aug 23 2017 23:22:25 GMT+0800 (中国标准时间)d
// 2.重写Date的toString()方法,使其返回一个object的值
Date.prototype.toString = function (e) {
return { year: this.getFullYear() };
}
// 略过了 dt.toString(),调用了 dt.valueOf()
dt + 'd'; // => 1503501745901d
Array + Array #
两方都为 object,则优先进行 valueOf()
计算,发现还是 object,继续进行 toString()
计算。
let a = [1,2,3]
let b = [4,5,6]
let c = a + b
console.log(c); // 1,2,34,5,6
console.log(typeof c); // string
来点奇葩的 #
[] + {}
[] + []
{} + {}
{} + []
{} + {}
好奇结果的自测下吧,我觉得没什么意义,看网上的答案自己实验了之后也不一样,实际场景中需要注意尽量避免这些问题,而应该使用规范的内置方法或者 lodash 之类的库进行计算。
自增 ++
#
++i
先给 i 自加1,再计算表达式的值。
i++
先计算表达式的值,再给 i 自加 1。