在JavaScript中,运算符优先级确保了当单个表达式包含多个运算符时执行的优先级。具有更高优先级的运算符会先于其他运算符执行。
当你编写只包含一到两个运算符的任何JavaScript表达式时,很容易理解表达式的输出。但是当表达式包含多个运算符时,你需要了解运算符优先级的概念来正确评估表达式。
运算符优先级示例
在传统数学中,乘法运算符比加法或减法运算符有更高的优先级。因此,如果任何数学表达式同时包含乘法和加法运算符,你需要先进行乘法运算。
结合性
结合性是指编译器在评估表达式时应遵循的方向。在许多情况下,运算符具有相同的优先级。在这种情况下,会出现不确定性,即编译器应该先执行哪个操作。因此,编译器会使用结合性规则。它可以是从左到右或从右到左。
例如,考虑以下表达式:
let res = 50 / 5 * 2;
将上述表达式视为 (50 / 5) * 2
会产生 20
作为结果;而将其视为 50 / (5 * 2)
则会产生 5
作为结果。为了解决这种不确定性,编译器使用了从左到右的结合性规则。因此,它将表达式评估为 (50 / 5) * 2
。
赋值运算符具有从右到左的结合性。考虑下面的赋值表达式:
p = q = 90;
在上述表达式中,90
赋给了 q
,并且 q
变量的值也赋给了 p
。
简而言之,JavaScript 编译器基于运算符优先级评估表达式,并且当多个运算符具有相同优先级时,它使用结合性规则。
运算符优先级表
下表列出了运算符、其描述、结合性方向以及一个简短的例子。
表格
优先级 |
运算符 |
描述 |
结合性 |
示例 |
1 |
() |
分组 |
左到右 |
(expression) |
2 |
. |
对象成员 |
左到右 |
Object_name.property |
2 |
() |
函数调用 |
左到右 |
Demo() |
2 |
new |
创建对象 |
右到左 |
New test() |
2 |
[] |
对象成员 |
左到右 |
Object["property"] |
3 |
-- |
后缀递减 |
- |
p--; |
3 |
++ |
后缀递增 |
- |
p++ |
4 |
-- |
前缀递减 |
右到左 |
--p; |
4 |
++ |
前缀递增 |
右到左 |
++p; |
4 |
typeof |
获取变量类型 |
右到左 |
typeof a; |
4 |
! |
逻辑非 |
右到左 |
!a; |
4 |
~ |
按位非 |
右到左 |
~p |
4 |
- |
单目负号 |
右到左 |
-p |
4 |
+ |
单目正号 |
右到左 |
+p |
4 |
delete |
删除对象属性 |
右到左 |
Delete arr[0] |
4 |
void |
评估为无效 |
右到左 |
Void(1) |
5 |
** |
幂运算符 |
右到左 |
p ** q |
6 |
* |
乘法 |
左到右 |
p * q |
6 |
/ |
除法 |
左到右 |
p / q |
6 |
% |
取模 |
左到右 |
p % q |
7 |
+ |
加法/正号 |
左到右 |
p + q |
7 |
- |
减法 |
左到右 |
p - q |
8 |
<< |
左移 |
左到右 |
p << 2 |
8 |
>> |
带符号右移 |
左到右 |
p >> 2 |
8 |
>>> |
无符号右移 |
左到右 |
p >>> 2 |
9 |
in |
对象中的属性 |
左到右 |
x in y |
9 |
instanceof |
对象的实例 |
左到右 |
p instanceof Object |
9 |
< |
小于 |
左到右 |
p < q |
9 |
<= |
小于等于 |
左到右 |
p <= q |
9 |
> |
大于 |
左到右 |
p > q |
9 |
>= |
大于等于 |
左到右 |
p >= q |
10 |
== |
等于 |
左到右 |
p == q |
10 |
!= |
不等于 |
左到右 |
p != q |
10 |
=== |
严格等于 |
左到右 |
p === q |
10 |
!== |
严格不等于 |
左到右 |
p !== q |
11 |
& |
按位与 |
左到右 |
p & q |
12 |
^ |
按位异或 |
左到右 |
p ^ q |
13 |
|
|
按位或 |
左到右 |
14 |
&& |
逻辑与 |
左到右 |
p && q |
15 |
|
|
|
逻辑或 |
16 |
?? |
Nullish 合并 |
右到左 |
p ?? q |
17 |
= |
赋值 |
右到左 |
p = q |
17 |
: |
冒号赋值 |
右到左 |
p : q |
17 |
+= |
加法赋值 |
右到左 |
p += q |
17 |
-= |
减法赋值 |
右到左 |
p -= q |
17 |
*= |
乘法赋值 |
右到左 |
p *= q |
17 |
/= |
除法赋值 |
右到左 |
p /= q |
17 |
%= |
取模赋值 |
右到左 |
p %= q |
17 |
**= |
幂运算赋值 |
右到左 |
p **= q |
17 |
<<= |
左移赋值 |
右到左 |
p <<= q |
17 |
>>= |
右移赋值 |
右到左 |
p >>= q |
17 |
>>>= |
无符号右移赋值 |
右到左 |
p >>>= q |
17 |
&= |
按位与赋值 |
右到左 |
p &= q |
17 |
^= |
按位异或赋值 |
右到左 |
p ^= q |
17 |
|
= |
按位或赋值 |
右到左 |
17 |
&&= |
逻辑与赋值 |
右到左 |
p &&= q |
17 |
|
|
= |
逻辑或赋值 |
17 |
=> |
箭头函数 |
- |
(a, b) => { // function code} |
17 |
… |
展开运算符 |
- |
[... arr] |
18 |
yield |
暂停/恢复 |
右到左 |
yield p; |
19 |
, |
逗号运算符 |
左到右 |
(10, 20, 30) |
示例
让我们通过简单的例子来理解运算符优先级。
示例
在下面的例子中,第一个表达式包含了具有相同优先级的除法、取模和乘法运算符。因此,编译器将使用从左到右的结合性规则。
第二个表达式中的幂运算符具有从右到左的结合性。因此,它评估表达式的方式与 (2 * 8 (3 ** 2))
相同。
<html>
<body>
<div id="output"></div>
<script>
const first = 30 / 15 % 3 * 2;
const second = 2 ** 3 ** 2;
document.getElementById("output").innerHTML =
"The value of first expression is : " + first + "<br>" +
"The value of second expression is : " + second;
</script>
</body>
</html>
输出
这将产生以下结果:
The value of first expression is : 4
The value of second expression is : 512
示例
此代码演示了你可以使用分组运算符 ()
来改变运算符优先级。在下面的代码中,我们采用了与上面代码相同的表达式,但改变了运算符的优先级。
在第一个表达式中,首先取模并乘以结果值 2。因此,我们得到 0 并用 30 除以 0,返回无穷大。
在第二个表达式中,首先计算 (2 ** 3)
和 (8 ** 2)
,其结果等于 64。
<html>
<body>
<div id="output"></div>
<script>
const first = 30 / ((15 % 3) * 2);
const second = (2 ** 3) ** 2;
document.getElementById("output").innerHTML =
"The value of first expression is : " + first + "<br>" +
"The value of second expression is : " + second;
</script>
</body>
</html>
输出
The value of first expression is : Infinity
The value of second expression is : 64
分组运算符可以改变任何运算符的优先级,因为它具有最高的运算符优先级。