外观
由 [-a+++b++] 引发的思考 - JavaScript运算符优先级
前言
最近在写代码的时候,遇到了一个比较有意思的问题,就是关于运算符优先级的问题。具体代码如下:
let a = 1;
let b = 2;
console.log(-a+++b++);
运行结果如下:
1
按照运算符优先级,-a+++b++
可以拆分为 -(a++) + (b++)
。因为 a++
和 b++
是后置递增运算符,所以 a
和 b
的值在计算时不会改变,从左到右依次计算,所以最终结果为 1
。
运算符优先级
运算符优先级决定了在表达式中运算符的执行顺序。JavaScript 运算符的优先级从高到低详细如下:
优先级 | 运算符类型 | 结合性 | 语法 |
---|---|---|---|
1 | 分组 () | - | (…) |
2 | 成员访问 . | 从左到右 | ….… |
2 | 需要计算的成员访问 [] | 从左到右 | …[…] |
2 | new (带参数列表) | - | new … ( … ) |
2 | 函数调用 | 从左到右 | … ( … ) |
2 | 可选链 ?. | 从左到右 | ?. |
3 | new 无参数列表 | 从右到左 | new … |
4 | 后置递增 ++ 和 后置递减 -- | - | … ++ 、… -- |
5 | 逻辑非 ! 和 按位非 ~ | 从右到左 | ! … 、~ … |
5 | 一元加法 + 和 一元减法 - | 从右到左 | + … 、- … |
5 | 前置递增 ++ 和 前置递减 -- | 从右到左 | ++ … 、-- … |
5 | typeof 、void 、delete 、await | 从右到左 | typeof … 、void … 、delete … 、await … |
6 | 幂运算 ** | 从右到左 | … ** … |
7 | 乘法 * 、 除法 / 、 取余 % | 从左到右 | … * … 、… / … 、… % … |
8 | 加法 + 、 减法 - | 从左到右 | … + … 、… - … |
9 | 按位左移 << 、 按位右移 >> 、 无符号右移 >>> | 从左到右 | … << … 、… << … 、… >>> … |
10 | 小于 < 、 小于等于 <= 、 大于 > 、 大于等于 >= | 从左到右 | … < … 、… <= … 、… > … 、… >= … |
10 | in 和 instanceof | 从左到右 | … in … 、… instanceof … |
11 | 相等 == 、 不相等 != 、 严格相等 === 、 严格不相等 !== | 从左到右 | … == … 、 … != … 、 … === … 、 … !== … |
12 | 按位与 & | 从左到右 | … & … |
13 | 按位异或 ^ | 从左到右 | … ^ … |
14 | 按位或 | | 从左到右 | | |
15 | 逻辑与 && | 从左到右 | && |
16 | 逻辑或 || | 从左到右 | || |
16 | 合并空值 ?? | 从左到右 | … ?? … |
17 | 条件(三元)运算符 ? : | 从右到左 | … ? … : … |
18 | 所有赋值运算符 | 从右到左 | … = … 、… += … 、… -= … 、… **= … 、… *= … 、… /= … 、… %= … 、… <<= … 、… >>= … 、… >>>= … 、… &= … 、… ^= … 、… |= … 、… &&= … 、… ||= … 、… ??= … |
19 | 逗号 | 从左到右 | … , … |
运算符示例
- 按位非运算符
~
let a = 5; // 二进制表示为 0000 0000 0000 0000 0000 0000 0000 0101
let result = ~a; // 取反后为 1111 1111 1111 1111 1111 1111 1111 1010
console.log(result); // 输出 -6
- 一元加法运算符
+
和 一元减法运算符-
let a = 5;
let result1 = +a; // 一元加法运算符,结果为 5
let result2 = -a; // 一元减法运算符,结果为 -5
console.log(result1); // 输出 5
console.log(result2); // 输出 -5
- void 运算符 void 运算符是 JavaScript 中的一个一元运算符,它的主要作用是计算一个表达式并返回 undefined。
let result = void 0; // 计算表达式 0,并返回 undefined
console.log(result); // 输出: undefined
- 按位左移运算符
<<
、 按位右移运算符>>
和 无符号右移运算符>>>
let a = 5; // 二进制表示为 0000 0000 0000 0000 0000 0000 0000 0101
let result1 = a << 1; // 左移 1 位,结果为 0000 0000 0000 0000 0000 0000 0000 1010,即 10
let result2 = a >> 1; // 右移 1 位,结果为 0000 0000 0000 0000 0000 0000 0000 0010,即 2
let result3 = a >>> 1; // 无符号右移 1 位,结果为 0000 0000 0000 0000 0000 0000 0000 0010,即 2
console.log(result1); // 输出 10
console.log(result2); // 输出 2
console.log(result3); // 输出 2
// 为了演示无符号右移运算符的效果,我们来看一个负数的例子:
let b = -5 // 二进制表示为 1111 1111 1111 1111 1111 1111 1111 1011
let result4 = b << 1; // 左移 1 位,结果为 1111 1111 1111 1111 1111 1111 1111 0110,即 -10
let result5 = b >> 1; // 右移 1 位,结果为 1111 1111 1111 1111 1111 1111 1111 1101,即 -3
let result6 = b >>> 1; // 无符号右移 1 位,结果为 0111 1111 1111 1111 1111 1111 1111 1101,即 2147483645
console.log(result4); // 输出 -10
console.log(result5); // 输出 -3
console.log(result6); // 输出 2147483645
- in 和 instanceof 运算符
in 运算符用于检测某个属性是否存在于某个对象中,instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
let obj = { a: 1, b: 2 };
console.log('a' in obj); // 输出 true
console.log('c' in obj); // 输出 false
let arr = [1, 2, 3];
console.log(0 in arr); // 输出 true
console.log(3 in arr); // 输出 false
function Person(name) {
this.name = name;
}
let person = new Person('Alice');
console.log(person instanceof Person); // 输出 true
console.log(person instanceof Object); // 输出 true
console.log(person instanceof Array); // 输出 false
- 按位与运算符
&
、 按位异或运算符^
和 按位或运算符|
let a = 5; // 二进制表示为 0000 0000 0000 0000 0000 0000 0000 0101
let b = 3; // 二进制表示为 0000 0000 0000 0000 0000 0000 0000 0011
let result1 = a & b; // 按位与运算,结果为 0000 0000 0000 0000 0000 0000 0000 0001,即 1
let result2 = a ^ b; // 按位异或运算,结果为 0000 0000 0000 0000 0000 0000 0000 0110,即 6
let result3 = a | b; // 按位或运算,结果为 0000 0000 0000 0000 0000 0000 0000 0111,即 7
console.log(result1); // 输出 1
console.log(result2); // 输出 6
console.log(result3); // 输出 7
- 合并空值运算符
??
合并空值运算符 ??
用于返回第一个不为 null 或 undefined 的值。
let a = null;
let b = 0;
let c = 'hello';
let result1 = a ?? b; // 输出 0
let result2 = b ?? c; // 输出 'hello'
let result3 = c ?? a; // 输出 'hello'