From 14023581030a22f3f48242f77ca8e7daef182dc9 Mon Sep 17 00:00:00 2001 From: plylrnsdy Date: Mon, 14 Jun 2021 12:00:12 +0800 Subject: [zh-cn] 更新“操作符优先级” MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修正操作符 `%` 的中文名称为“取余” - 修正操作符 `==`/`!=`/`===`/`!==` 的中文名称,与指向页面保持一致 - 同步英文版 - 新增内容 - 展开语法(Spread syntax)从操作符优先级表格移除,并解释原因 - 表格、链接等 html 格式 --- .../operators/operator_precedence/index.html | 774 ++++++++++++--------- 1 file changed, 455 insertions(+), 319 deletions(-) (limited to 'files') diff --git a/files/zh-cn/web/javascript/reference/operators/operator_precedence/index.html b/files/zh-cn/web/javascript/reference/operators/operator_precedence/index.html index de411354aa..a2cd129c56 100644 --- a/files/zh-cn/web/javascript/reference/operators/operator_precedence/index.html +++ b/files/zh-cn/web/javascript/reference/operators/operator_precedence/index.html @@ -2,350 +2,486 @@ title: 运算符优先级 slug: Web/JavaScript/Reference/Operators/Operator_Precedence tags: + - 指南 - JavaScript - 优先级 - - 运算符 translation_of: Web/JavaScript/Reference/Operators/Operator_Precedence ---
{{jsSidebar("Operators")}}
-

运算符的优先级决定了表达式中运算执行的先后顺序,优先级高的运算符最先被执行。

+

运算符的优先级决定了表达式中运算执行的先后顺序。优先级高的运算符会作为优先级低的运算符的操作符。

{{EmbedInteractiveExample("pages/js/expressions-operatorprecedence.html")}}
+

优先级和结合性

-

关联性

+

考虑由下面的表示法描述的表达式。其中,OP1 和 OP2 都是操作符的占位符。

-

关联性决定了拥有相同优先级的运算符的执行顺序。考虑下面这个表达式:

+
a OP1 b OP2 c
-
a OP b OP c;
+

如果 OP1OP2 具有不同的优先级(见下表),则优先级最高的运算符先执行,不用考虑结合性。观察乘法如何具有比加法更高的优先级并首先执行,即使加法是首先写入代码的。

+ +
console.log(3 + 10 * 2);   // 输出 23
+console.log(3 + (10 * 2)); // 输出 23 因为这里的括号是多余的
+console.log((3 + 10) * 2); // 输出 26 因为括号改变了优先级
+
+ +

左结合(左到右)相当于把左边的子表达式加上小括号 (a OP b) OP c,右结合(右到左)相当于 a OP (b OP c)。赋值运算符是右结合的,所以你可以这么写:

+ +
a = b = 5; // 相当于 a = (b = 5);
 
-

左关联(左到右)相当于把左边的子表达式加上小括号(a OP b) OP c,右关联(右到左)相当于a OP (b OP c)。赋值运算符是右关联的,所以你可以这么写:

+

预期结果是 a 和 b 的值都会成为 5。这是因为赋值运算符的返回结果就是赋值运算符右边的那个值,具体过程是:首先 b 被赋值为5,然后 a 也被赋值为 b = 5 的返回值,也就是 5。

+ +

另一个例子是,只有幂运算符是右结合的,而其他算术运算符都是左结合的。有趣的是,无论结合性和优先级如何,求值顺序总是从左到右。

-
a = b = 5; 
+ + + + + + + + + + + + + + + +
代码输出
+
+function echo(name, num) {
+    console.log("Evaluating the " + name + " side");
+    return num;
+}
+// 注意这里的除法运算符 (/)
+console.log(echo("left", 6) / echo("right", 2));
+
+
+
+Evaluating the left side
+Evaluating the right side
+3
+
+
+
+function echo(name, num) {
+    console.log("Evaluating the " + name + " side");
+    return num;
+}
+// 注意这里的幂运算符 (**)
+console.log(echo("left", 2) ** echo("right", 3));
+
+
+Evaluating the left side
+Evaluating the right side
+8
+
-

结果 a 和 b 的值都会成为5。这是因为赋值运算符的返回结果就是赋值运算符右边的那个值,具体过程是:b被赋值为5,然后a也被赋值为 b=5 的返回值,也就是5。

+

当有多个具有相同优先级的运算符时,结合性的差异就会发挥作用。仅使用一个或多个不同优先级的运算符时,结合性不会影响输出,如上面的例子所示。在下面的示例中,观察当使用多个相同运算符时关联性如何影响输出。

+ + + + + + + + + + + + + + + + + + + + +
代码输出
+
+function echo(name, num) {
+    console.log("Evaluating the " + name + " side");
+    return num;
+}
+// 注意这里的除法运算符 (/)
+console.log(echo("left", 6) / echo("middle", 2) / echo("right", 3));
+
+
+
+Evaluating the left side
+Evaluating the middle side
+Evaluating the right side
+1
+
+
+
+function echo(name, num) {
+    console.log("Evaluating the " + name + " side");
+    return num;
+}
+// 注意这里的幂运算符 (**)
+console.log(echo("left", 2) ** echo("middle", 3) ** echo("right", 2));
+
+
+
+Evaluating the left side
+Evaluating the middle side
+Evaluating the right side
+512
+
+
+
+function echo(name, num) {
+    console.log("Evaluating the " + name + " side");
+    return num;
+}
+// 注意这里左边和中间的被包围的求幂表达式
+console.log((echo("left", 2) ** echo("middle", 3)) ** echo("right", 2));
+
+
+Evaluating the left side
+Evaluating the middle side
+Evaluating the right side
+64
+
+ +

观察上面的代码片段,6 / 3 / 2 与 + (6 / 3) / 2 是相同的,因为除法是左结合的。而幂运算符是右结合的,所以 2 ** 3 ** 2 与 + 2 ** (3 ** 2) 是相同的。因此,(2 ** 3) ** 2 会更改执行顺序,并导致输出上表中的 64。

+ +

请记住,判断执行顺序,优先级在结合性之前。所以,混合求除法和幂,求幂会先于除法。例如, + 2 ** 3 / 3 ** 2 结果是 0.8888888888888888 因为它相当于 + (2 ** 3) / (3 ** 2)

+ +

分组和短路的注意事项

+ +

在下表中,分组(Grouping)具有最高优先级。然而,这并不意味着总是优先对分组符号 ( … ) 内的表达式进行求值,尤其是涉及短路时。

+ +

短路是条件求值的术语。例如,在表达式 + a && (b + c) 中,如果 a 为 {{Glossary("falsy")}},那么即使 (b + c) 在圆括号中,也不会被求值。我们可以说逻辑或运算符(“OR”)是“短路的”。除了逻辑或运算符外,其他短路运算符还包括逻辑与(“AND”)、空值合并、可选链和条件(三元)运算符。下面有更多例子:

+ +
a || (b * c);  // 首先对 `a` 求值,如果 `a` 为真值则直接返回 `a`
+a && (b < c);  // 首先对 `a` 求值,如果 `a` 为虚值则直接返回 `a`
+a ?? (b || c); // 首先对 `a` 求值,如果 `a` 不是 `null` 或 `undefined` 则直接返回 `a`
+a?.b.c;        // 首先对 `a` 求值,如果 `a` 是 `null` 或 `undefined` 则直接返回 `undefined`
+

示例

-
3 > 2 && 2 > 1
-// return true
+
3 > 2 && 2 > 1
+// 返回 true
 
 3 > 2 > 1
-// 返回 false,因为 3 > 2 是 true,并且 true > 1 is false
+// 返回 false,因为 3 > 2 是 true,然后 true 会在比较运算符中
+// 被隐式转换为 1,因此 true > 1 会变为 1 > 1,结果是 false
 // 加括号可以更清楚:(3 > 2) > 1
 
-

汇总表

+

汇总表

-

下面的表将所有运算符按照优先级的不同从高(20)到低(1)排列。

+

下面的表格将所有运算符按照优先级的不同从高(21)到低(1)排列。

+ +

请注意,下表中故意不包含展开语法(Spread syntax) —— 原因可以引用Stack Overflow 上的一个回答,“展开语法不是一个运算符,因此没有优先级。它是数组字面量和函数调用(和对象字面量)语法的一部分。”

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
优先级运算类型关联性运算符
21{{jsxref("Operators/Grouping", "圆括号")}}n/a(不相关)( … )
20{{jsxref("Operators/Property_Accessors","成员访问", "#点符号表示法")}}从左到右… . …
{{jsxref("Operators/Property_Accessors","需计算的成员访问", "#括号表示法")}}从左到右… [ … ]
{{jsxref("Operators/new","new")}} (带参数列表)n/anew … ( … )
函数调用从左到右… ( … )
可选链(Optional chaining)从左到右?.
19new (无参数列表)从右到左new …
18后置递增(运算符在后)n/a
-  
… ++
后置递减(运算符在后)… --
17逻辑非从右到左! …
按位非~ …
一元加法+ …
一元减法- …
前置递增++ …
前置递减-- …
typeoftypeof …
voidvoid …
deletedelete …
awaitawait …
16从右到左… ** …
15乘法从左到右
-  
… * …
除法… / …
取模… % …
14加法从左到右
-  
… + …
减法… - …
13按位左移从左到右… << …
按位右移… >> …
无符号右移… >>> …
12小于从左到右… < …
小于等于… <= …
大于… > …
大于等于… >= …
in… in …
instanceof… instanceof …
11等号从左到右
-  
… == …
非等号… != …
全等号… === …
非全等号… !== …
10按位与从左到右… & …
9按位异或从左到右… ^ …
8按位或从左到右… | …
7逻辑与从左到右… && …
6逻辑或从左到右… || …
5空值合并从左到右… ?? …
4条件运算符从右到左… ? … : …
3赋值从右到左… = …
… += …
… -= …
… **= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
… |= …
… &&= …
… ||= …
… ??= …
2yield从右到左yield …
yield*yield* …
1展开运算符n/a... …
0逗号从左到右… , …
+ + + 优先级 + 运算符类型 + 结合性 + 运算符 + + + 21 + {{jsxref("Operators/Grouping", "分组", "", 1)}} + n/a(不相关) + ( … ) + + + 20 + {{jsxref("Operators/Property_Accessors", "成员访问", "#Dot_notation", 1)}} + 从左到右 + … . … + + + {{jsxref("Operators/Property_Accessors", "需计算的成员访问", "#Bracket_notation", 1)}} + 从左到右 + … [ … ] + + + {{jsxref("Operators/new","new")}}(带参数列表) + n/a + new … ( … ) + + + 函数调用 + 从左到右 + … ( ) + + + 可选链(Optional chaining) + 从左到右 + ?. + + + 19 + {{jsxref("Operators/new","new")}}(无参数列表) + 从右到左 + new … + + + 18 + {{jsxref("Operators","后置递增","#Increment", 1)}} + n/a + … ++ + + + {{jsxref("Operators","后置递减","#Decrement", 1)}} + … -- + + + 17 + 逻辑非 (!) + 从右到左 + ! … + + + 按位非 (~) + ~ … + + + 一元加法 (+) + + … + + + 一元减法 (-) + - … + + + 前置递增 + ++ … + + + 前置递减 + -- … + + + {{jsxref("Operators/typeof", "typeof")}} + typeof … + + + {{jsxref("Operators/void", "void")}} + void … + + + {{jsxref("Operators/delete", "delete")}} + delete … + + + {{jsxref("Operators/await", "await")}} + await … + + + 16 + 幂 (**) + 从右到左 + … ** … + + + 15 + 乘法 (*) + 从左到右 + … * … + + + 除法 (/) + … / … + + + 取余 (%) + … % … + + + 14 + 加法 (+) + 从左到右 + … + … + + + 减法 (-) + … - … + + + 13 + 按位左移 (<<) + 从左到右 + … << … + + + 按位右移 (>>) + … >> … + + + 无符号右移 (>>>) + … >>> … + + + 12 + 小于 (<) + 从左到右 + … < … + + + 小于等于 (<=) + … <= … + + + 大于 (>) + … > … + + + 大于等于 (>=) + … >= … + + + {{jsxref("Operators/in", "in")}} + … in … + + + {{jsxref("Operators/instanceof", "instanceof")}} + … instanceof … + + + 11 + 相等 (==) + 从左到右 + … == … + + + 不相等 (!=) + … != … + + + 一致/严格相等 (===) + … === … + + + 不一致/严格不相等 (!==) + … !== … + + + 10 + 按位与 (&) + 从左到右 + … & … + + + 9 + 按位异或 (^) + 从左到右 + … ^ … + + + 8 + 按位或 (|) + 从左到右 + … | … + + + 7 + 逻辑与 (&&) + 从左到右 + … && … + + + 6 + 逻辑或 (||) + 从左到右 + … || … + + + 5 + 空值合并 (??) + 从左到右 + … ?? … + + + 4 + 条件(三元)运算符 + 从右到左 + … ? … : … + + + 3 + 赋值 + 从右到左 + … = … + + + … += … + + + … -= … + + + … **= … + + + … *= … + + + … /= … + + + … %= … + + + … <<= … + + + … >>= … + + + … >>>= … + + + … &= … + + + … ^= … + + + … |= … + + + … &&= … + + + … ||= … + + + … ??= … + + + 2 + {{jsxref("Operators/yield", "yield")}} + 从右到左 + yield … + + + {{jsxref("Operators/yield*", "yield*")}} + yield* … + + + 1 + 逗号 / 序列 + 从左到右 + … , … + + + \ No newline at end of file -- cgit v1.2.3-54-g00ecf