# 从new xx()和new xx的区别中,整理JS中操作符的优先级 ## 问题 最近在工作中写日期格式化时,遇到一个问题,先来看下面的代码: ```typescript // 写法一 new Date().toISOString // 写法二 new Date.toISOString; ``` 执行如下: ![](./new-()and-new.png) 猜测是优先级的问题,然后在MDN找到了[答案](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_precedence#%E6%B1%87%E6%80%BB%E8%A1%A8),它们确实是两个不同的优先级,如下图: ![](./js-diff-new()-and-new.png) 以上,方式一中的 `.` 优先级最高,但其左边需要先求值,因而先执行`new Date()`得到实例,再返回实例上的`toISOString`属性;而方式二中的 `. `优先级也是最高,但其左边不用求值,因而可以先执行`Date.toISOString`得到`toISOString`值(Date上不存在该属性,因此该值为undefined),再尝试进行`new toISOString`操作时报错发生。 ## 优先级 我印象中的运算符只有四十几个,没想到在MDN里面找到的有六十几个。说实话,上面的运算符以前真没注意过,趁着空闲我决定将这些运算符整理下,整理后总体分为下面的五大类: ### 一级运算符 大部分是一元操作符。 | 运算符 | 类型 | 说明 | :--- | :--- | --- | ( … ) | 分组 | 优先级最高的运算符 | … . … | 成员访问 | 静态访问 | [] | 需计算的成员访问 | 动态访问 | new xx() | new(带参数列表) | 实例化 | () | 函数调用 | 函数调用 | ?. | 可选链(Optional chaining) | `a?.b`类似于`a === null \|\| a === void 0 ? void 0 : a.b;` | new … | new(无参数列表) | 实例化 | … ++ | 后置递增 | 先返回再+1,例如 `let a = 1; const b = a++; // b: 1 a: 2`。比较常见的是在for循环中进行后置递增。 | … -- | 后置递减 | 同上 | ! … | 逻辑非 (!) | | ~ … | 按位非 (~) | | + … | 一元加法 (+) | 可用于把字符串转换为数值,例如`+'1'`将得到`1`。 | - … | 一元减法 (-) | 同上 | ++ … | 前置递增 | 与后置递增不同,先执行+1再返回 | -- … | 前置递减 | 同上 | typeof … | typeof | 返回值只有这几个:string \| number \| boolean \| undefined \| null \| function \| object | void … | void | 比较常见的是使用`void 0`代替undefined,因为在以前undefined是可以作为变量名使用的。 | delete … | delete | 删除对象的属性 | await … | await | 等待某个promise执行成功 ### 算符运算符 | 运算符 | 类型 | 说明 | :--- | :--- | --- | … ** … | 幂 (**) | | … * … | 乘法 (*) | | … / … | 除法 (/) | | … % … | 取余 (%) | | … + … | 加法 (+) | | … - … | 减法 (-) | | … << … | 按位左移 (<<) | 通常用于二进制数据的移位, 例如:`(4)<<1` 将得到`8`。过程:十进制`4`转换为二进制`100`, 左移一位得到`1000`,再转换为十进制即为`8`。 | … >> … | 按位右移 (>>) | 同上 | … >>> … | 无符号右移 (>>>) | 同上 | … < … | 小于 (<) | 对于数值,比较大小 | … <= … | 小于等于 (<=) | | … > … | 大于 (>) | | … >= … | 大于等于 (>=) | ### 比较运算符 | 运算符 | 类型 | 说明 | :--- | :--- | --- | … in … | in | 判断某个属性是否存在于对象上,会顺着原型链进行查找,可以用`Object.prototype.hasOwnProperty.call(obj, 'xx')`进行检测自身的属性是否存在。 | … instanceof … | instanceof | 判断右边的对象,是否在左边对象的原型链上。 | … == … | 相等 (==) | 左右两边的值可能会先做隐式转换,再进行比较,例如: `'1' == 1 //true` | … != … | 不相等 (!=) | 同上 | … === … | 一致/严格相等 (===) | 左右两边的值不做隐式转换,直接比较,例如:`'1' === 1 // false` | … !== … | 不一致/严格不相等 (!==) | 同上 ### 布尔运算符 | 运算符 | 类型 | 说明 | :--- | :--- | --- | … & … | 按位与 (&) | 常用于对二进制数值进行操作 | … ^ … | 按位异或 (^) | | … \| … | 按位或 (\|) | | … && … | 逻辑与 (&&) | | … \|\| … | 逻辑或 (||) | | … ?? … | 空值合并 (??) | 当左边的值不为undefined或null时,返回左边,否则返回右边, 具体代码可能是这样的\n: `a !== null && a !== void 0 ? a : b;` | ### 赋值运算符 | 运算符 | 类型 | 说明 | :--- | :--- | --- | … ? … : … | 条件(三元)运算符 | 可以在简单场景中代替if/else使用,不过自从出了??运算符,这个运算符用的比较少 | … = … | 赋值 | | … += … | | … -= … | | … **= … | | … *= … | | … /= … | | … %= … | | … <<= … | | … >>= … | | … >>>= … | | … &= … | | … ^= … | | … |= … | | … &&= … | | … ||= … | | … ??= … | | … , … | 逗号 / 序列 | 优先级最低的运算符,由于其会返回最后一个值,在某些简短操作中也会用到,例如:`const map = items.reduce((m, i) => (m[i.id]=i,m), {})` ## 结语 优先级的重要性不言而喻,往后还是要多多温习。