JavaScript 操作符优先级是怎么确定的?

文章导读
Previous Quiz Next 在 JavaScript 中,operator precedence(运算符优先级)确保了当单个表达式包含多个运算符时,这些运算符的执行优先顺序。因此,具有更高优先级的表达式会被编译器首先执行,然后再执行优先级较低的运算符。
📋 目录
  1. 结合性
  2. 运算符优先级表
  3. 示例
A A

JavaScript - 运算符优先级



Previous
Quiz
Next

在 JavaScript 中,operator precedence(运算符优先级)确保了当单个表达式包含多个运算符时,这些运算符的执行优先顺序。因此,具有更高优先级的表达式会被编译器首先执行,然后再执行优先级较低的运算符。

当你编写只包含 1 个或 2 个运算符的 JavaScript 表达式时,你可以轻松理解表达式的输出结果。但当表达式包含多个运算符时,你需要了解 operator precedence(运算符优先级)的概念,才能正确求值表达式。

operator precedence(运算符优先级)的最佳示例是,在传统数学中,乘法运算符的优先级高于加法或减法运算符。因此,如果数学表达式同时包含乘法和加法运算符,你需要先执行乘法运算。

结合性

结合性(associativity)指的是编译器在求值表达式时应该遵循的方向。在许多情况下,运算符具有相同的优先级。这种情况下,会出现歧义,即编译器应该先执行哪个操作。因此,编译器会借助结合性规则。它可以是从左到右或从右到左。

例如,我们需要执行以下表达式。

let res = 50/5*2;
  • 将上述表达式视为 (50/5) * 2,结果为 20。

  • 将表达式视为 50/ (5*2),结果为 5。

为了解决上述歧义,编译器使用结合性规则。除法和乘法运算符的结合性是从左到右。因此,它将表达式求值为 (50 / 5) * 2。

赋值运算符具有从右到左的结合性。考虑以下赋值表达式。

P = q = 90;

在上述表达式中,90 被赋值给 q,然后 q 的值被赋值给 p。

简而言之,JavaScript 编译器根据 operator precedence(运算符优先级)求值表达式,当多个运算符具有相同优先级时,它使用结合性规则。

运算符优先级表

下面的表格包含运算符、其描述、结合方向以及简短示例。

优先级 运算符 描述 结合性 示例
1 () 分组 L -> R (expression)
2 . 对象成员 L -> R Object_name.property
2 () 函数调用 L -> R Demo()
2 new 创建对象 R -> L New test()
2 [] 对象成员 L -> R Object["property"]
3 -- 后缀递减 - p--;
3 ++ 后缀递增 - p++
4 -- 前缀递减 R -> L --p;
4 ++ 前缀递增 R -> L ++p;
4 typeof 获取变量类型 R -> L typeof a;
4 ! 逻辑非 R -> L !a;
4 ~ 按位非 R -> L ~p
4 - 一元减法 R -> L -p
4 + 一元加法 R -> L +p
4 delete 删除对象属性 R -> L Delete arr[0]
4 void 计算 void R -> L Void(1)
5 ** 幂运算符 R -> L p ** q
6 * 乘法 L -> R p * q
6 / 除法 L -> R p / q
6 % 模运算 L -> R p % q
7 + 加法或正号运算符 L -> R p + q
7 - 减法运算符 L -> R p - q
8 << 左移 L -> R p << 2
8 >> 有符号右移 L -> R p >> 2
8 >>> 无符号右移 L -> R p >>> 2
9 in 属性在对象中 L -> R x in y
9 instanceof 对象实例 L -> R p instanceof Object
9 < 小于 L -> R p < q
9 <= 小于或等于 L -> R p <= q
9 > 大于 L -> R p > q
9 >= 大于或等于 L -> R p >= q
10 == 相等 L -> R p == q
10 != 不相等 L -> R p != q
10 === 严格相等 L -> R p === q
10 !== 严格不相等 L -> R p !== q
11 & 按位与 L -> R p & q
12 ^ 按位异或 L -> R p ^ q
13 | 按位或 L -> R p | q
14 && 逻辑与 L -> R p && q
15 || 逻辑或 L -> R p || q
16 ?? 空值合并 R -> L p ?? q
17 = 赋值 R -> L p = q
17 : 冒号赋值 R -> L p : q
17 += 加法赋值 R -> L p += q
17 -= 减法赋值 R -> L p -= q
17 *= 乘法赋值 R -> L p *= q
17 /= 除法赋值 R -> L p /= q
17 %= 模运算赋值 R -> L p %= q
17 **= 幂运算赋值 R -> L p **= q
17 <<= 左移赋值 R -> L p <<= q
17 >>= 右移赋值 R -> L p >>= q
17 >>>= 无符号右移赋值 R -> L p >>>= q
17 &= 按位与赋值 R -> L p &= q
17 ^= 按位异或赋值 R -> L p ^= q
17 |= 按位或赋值 R -> L p |= q
17 &&= 逻辑与赋值 R -> L p &&= q
17 ||= 逻辑或赋值 R -> L p ||= q
17 => 箭头运算符 - (a, b )=> { // function code}
17 展开运算符 - [ arr]
18 yield 暂停/恢复 R -> L yield p;
19 , 逗号运算符 L -> R (10, 20, 30)

示例

让我们通过简单的示例来理解运算符优先级。

示例

在下面的示例中,第一个表达式包含具有相同优先级的除法、取模和乘法运算符。因此,编译器将使用结合性规则,对于乘法、除法和取模运算符是从左到右结合。

因此,它先将 30 除以 15,然后计算 (30/15) 对 3 的取模,最后将 ((30/15)%3) 与 2 相乘。

在第二个表达式中,幂运算符具有从右到左的结合性。因此,它以与 (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,返回 Infinity。

在第二个表达式中,首先计算 (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
分组运算符可以改变任何运算符的优先级,因为它具有最高的运算符优先级。