JS 加号运算符的运用

JS 加号运算符的运用

Sep 18, 2021
前端, JavaScript

一元运算符 #

语法: + Expression

说明: + 号运算符作为一元运算符时,Expression将进行 ToNumber 操作。

argument类型返回值
Undefinedreturn NaN
Nullreturn +0
Booleantrue return 1; false return 0;
Numberreturn 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个步骤:

  1. 值进行 GetValue 操作。

  2. 值进行 ToPrimitive 操作,

  3. 若一方为 String 类型,2个值都进行 ToString 转换,最后进行字符串连接操作。

  4. 若都不是 String 类型,2个值都进行 ToNumber 转换,最后进行算数加法运算。

其中方法 ToPrimitive 语法为:

ToPrimitive ( input [, PreferredType] )
  1. input 表示传入的值。
  2. PreferredType 是可选值,表示需要被转换的类型。+ 为一元运算符时,此值为“number”;而作为二元运算符时,未传递此值,以默认的“default”代替。

官方文档 ecma262 的计算步骤如下图所示:

其中 OrdinaryToPrimitive 如下:

简单解释一下,若 input 类型为原始值(如:Undefined、Null、Boolean、Number、String、Symbol),直接返回 input 的本身;若 input 类型为object(如:Array、Object、Date),将根据第 2 个参数 PreferredType 的值进行以下操作:

  1. 调用 obj[Symbol.toPrimitive](hint) :带有 symbol 键 Symbol.toPrimitive(系统 symbol)的方法,如果这个方法存在的话。
  2. 否则,如果 PreferredType 是 "string" :调用 toString 方法,如果它不存在或结果是 object,则调用 valueOf 方法。
  3. 否则,如果 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。

参考 #

https://www.cnblogs.com/polk6/p/js-adv-addopr.html

https://zh.javascript.info/object-toprimitive

本文共 1554 字,上次修改于 Jan 28, 2024,以 CC 署名-非商业性使用-禁止演绎 4.0 国际 协议进行许可。

相关文章

» JS 非空判断

» 跨域相关问题

» 了解下 MobX 概念

» 了解下 Redux 概念

» 浏览器中的 HTTP 缓存使用策略