Skip to content

JavaScript 运算符优先级:掌握计算顺序的规则

还记得小学数学课上的"先乘除后加减"吗?在 JavaScript 中,运算符也有类似的优先级规则。就像交通规则决定了谁先通行,运算符优先级决定了复杂表达式中哪个运算先执行。理解这些规则,能让你编写出正确且易读的代码。

为什么需要优先级?

看一个简单的例子:

javascript
let result = 3 + 4 * 5;
console.log(result); // 23,不是 35!

为什么是 23 而不是 35?因为乘法 (*) 的优先级高于加法 (+),所以先计算 4 * 5 = 20,然后再加 3,得到 23。

如果我们想先计算加法,就需要使用括号:

javascript
let result = (3 + 4) * 5;
console.log(result); // 35

优先级表(从高到低)

以下是常用运算符的优先级排序,数字越大优先级越高:

javascript
// 优先级 20:分组(括号)
(expression)

// 优先级 19:成员访问、函数调用
obj.property
obj[property]
func(args)
new Constructor()

// 优先级 17:后置递增/递减
x++
x--

// 优先级 16:逻辑非、一元运算符、前置递增/递减
!x
+x
-x
typeof x
++x
--x

// 优先级 15:幂运算
**

// 优先级 14:乘法、除法、取余
*
/
%

// 优先级 13:加法、减法
+
-

// 优先级 12:位移运算
<<
>>
>>>

// 优先级 11:关系运算符
<
<=
>
>=
in
instanceof

// 优先级 10:相等性运算符
==
!=
===
!==

// 优先级 9-5:位运算符
&
^
|

// 优先级 6:逻辑与
&&

// 优先级 5:逻辑或
||

// 优先级 4:空值合并
??

// 优先级 4:条件运算符
? :

// 优先级 3:赋值运算符
=
+=
-=
*=
/=
等等

// 优先级 1:逗号运算符

常见优先级示例

算术运算符优先级

javascript
// 乘除优先于加减
console.log(2 + 3 * 4); // 14(先 3*4=12,再 2+12=14)
console.log(10 - 6 / 2); // 7(先 6/2=3,再 10-3=7)
console.log(8 / 2 + 3); // 7(先 8/2=4,再 4+3=7)

// 同级运算从左到右
console.log(10 - 3 - 2); // 5(先 10-3=7,再 7-2=5)
console.log(20 / 4 / 2); // 2.5(先 20/4=5,再 5/2=2.5)

// 幂运算优先于乘除
console.log(2 * 3 ** 2); // 18(先 3**2=9,再 2*9=18)
console.log(2 ** 3 * 4); // 32(先 2**3=8,再 8*4=32)

// 使用括号改变优先级
console.log((2 + 3) * 4); // 20
console.log(2 * (3 + 4)); // 14

比较和逻辑运算符

javascript
// 比较优先于逻辑运算
let age = 25;
let hasLicense = true;

// 先执行 age >= 18,得到 true,再执行 true && hasLicense
console.log(age >= 18 && hasLicense); // true

// && 优先于 ||
console.log(true || (false && false)); // true(先 false && false = false,再 true || false = true)
console.log((true || false) && false); // false(改变优先级)

// ! 优先于 &&
let isWeekend = false;
let isHoliday = false;
console.log(!isWeekend && !isHoliday); // true(先执行两个 !)

// 实际应用
let score = 85;
let attendance = 90;
let passed = score >= 80 && attendance >= 85;
console.log(passed); // true

赋值运算符优先级最低

赋值运算符的优先级很低,几乎总是最后执行:

javascript
let a, b, c;

// 先计算右边的表达式,最后赋值
a = 5 + 3; // 先 5+3=8,再赋值给 a
console.log(a); // 8

b = 10 > 5; // 先比较,得 true,再赋值给 b
console.log(b); // true

c = 2 + 3 * 4 > 10; // 先 3*4=12,再 2+12=14,再 14>10=true,最后赋值
console.log(c); // true

一元运算符优先级

一元运算符优先级较高:

javascript
// typeof 优先级高于算术运算
console.log(typeof 5 + 3); // "number3"(先 typeof 5 = "number",再拼接)
console.log(typeof (5 + 3)); // "number"(改变优先级)

// ! 优先级很高
console.log(!true || false); // false(先 !true=false,再 false || false)

// 递增/递减
let x = 5;
let y = ++x * 2; // 先 ++x 得 6,再 6*2=12
console.log(y); // 12
console.log(x); // 6

let a = 5;
let b = a++ * 2; // 先使用 a=5 进行运算 5*2=10,再递增 a
console.log(b); // 10
console.log(a); // 6

结合性(Associativity)

当运算符优先级相同时,结合性决定了计算顺序。大部分运算符是左结合(从左到右),少数是右结合(从右到左)。

左结合性

javascript
// 算术运算符:从左到右
console.log(10 - 5 - 2); // 3((10 - 5) - 2 = 5 - 2 = 3)
console.log(20 / 4 / 2); // 2.5((20 / 4) / 2 = 5 / 2 = 2.5)

// 加法:从左到右
console.log(1 + 2 + 3 + 4); // 10(((1 + 2) + 3) + 4)

// 字符串拼接
console.log("a" + "b" + "c"); // "abc"(("a" + "b") + "c")
console.log(1 + 2 + "3"); // "33"((1 + 2) + "3" = 3 + "3")
console.log("1" + 2 + 3); // "123"(("1" + 2) + 3 = "12" + 3)

右结合性

javascript
// 赋值运算符:从右到左
let a, b, c;
a = b = c = 10;
// 执行顺序:c = 10,然后 b = 10,最后 a = 10
console.log(a, b, c); // 10 10 10

// 条件运算符:从右到左
let result = true ? 1 : false ? 2 : 3;
// 执行顺序:false ? 2 : 3 = 3,然后 true ? 1 : 3 = 1
console.log(result); // 1

// 幂运算:从右到左(这是特殊的!)
console.log(2 ** (3 ** 2)); // 512(2 ** (3 ** 2) = 2 ** 9 = 512)
console.log((2 ** 3) ** 2); // 64(改变结合性)

复杂表达式分析

让我们分析一些复杂的表达式:

示例 1:混合运算

javascript
let result = 5 + 3 * 2 - 4 / 2;

// 分步执行:
// 1. 3 * 2 = 6(乘法优先)
// 2. 4 / 2 = 2(除法优先)
// 3. 5 + 6 = 11(加法)
// 4. 11 - 2 = 9(减法)

console.log(result); // 9

示例 2:逻辑和比较

javascript
let x = 10;
let y = 20;
let z = 30;

let result = (x < y && y < z) || z < x;

// 分步执行:
// 1. x < y => 10 < 20 = true
// 2. y < z => 20 < 30 = true
// 3. z < x => 30 < 10 = false
// 4. true && true = true
// 5. true || false = true

console.log(result); // true

示例 3:赋值和递增

javascript
let a = 5;
let b = a++ + ++a;

// 分步执行:
// 1. a++ 使用当前值 5,然后 a 变成 6
// 2. ++a 先递增(a 变成 7),然后使用 7
// 3. 5 + 7 = 12
// 4. 赋值给 b

console.log(b); // 12
console.log(a); // 7

示例 4:条件和逻辑

javascript
let score = 85;
let result = score >= 90 ? "A" : score >= 80 ? "B" : "C";

// 分步执行(从右到左):
// 1. 内层条件:score >= 80 ? "B" : "C"
//    85 >= 80 为 true,所以得到 "B"
// 2. 外层条件:score >= 90 ? "A" : "B"
//    85 >= 90 为 false,所以得到 "B"

console.log(result); // "B"

实际编程建议

1. 使用括号增强可读性

即使你知道优先级规则,为了让代码更易读,建议使用括号:

javascript
// 不够清晰
let result1 = a + b * c - d / e;

// 更清晰
let result2 = a + b * c - d / e;

// 复杂逻辑一定要用括号
let canAccess = (isAdmin || isModerator) && !isBanned;

2. 避免过于复杂的表达式

javascript
// ❌ 不好:太复杂
let result = (a++ + (++b * c--) / --d && e > f) || g <= h ? i : j;

// ✅ 好:拆分成多个步骤
let temp1 = a++ + (++b * c--) / --d;
let temp2 = temp1 && e > f;
let result = temp2 || g <= h ? i : j;

// 或者更好:完全拆分
let multiplied = ++b * c--;
let divided = multiplied / --d;
let sum = a++ + divided;
let condition1 = sum && e > f;
let condition2 = condition1 || g <= h;
let result = condition2 ? i : j;

3. 注意类型转换的陷阱

javascript
// 字符串拼接优先于数字相加
console.log(1 + 2 + "3"); // "33"
console.log("1" + 2 + 3); // "123"
console.log("1" + (2 + 3)); // "15"

// 使用括号明确意图
let total = price + tax; // 数字相加
let display = "Total: $" + (price + tax); // 先计算,再拼接

4. 小心短路求值

javascript
// && 的短路特性
let user = null;
let name = user && user.name; // 安全:user 为 null,不会访问 name

// 但不要过度依赖
let value = obj && obj.prop && obj.prop.value; // 可读性差

// 使用可选链更清晰
let value = obj?.prop?.value;

5. 赋值的返回值

赋值运算符会返回所赋的值,可以用于连续赋值,但要小心:

javascript
// 连续赋值
let a = (b = c = 0); // 都是 0

// 在条件中赋值(容易混淆)
if ((x = 10)) {
  // 这是赋值!不是比较!
  console.log("Always true");
}

// 应该是:
if (x === 10) {
  console.log("Compare");
}

// 有意的条件赋值要用括号表明
if ((match = text.match(/pattern/))) {
  console.log(match);
}

优先级记忆技巧

基本规则

  1. 括号最高() 总是先计算
  2. 成员访问和函数调用. [] ()
  3. 一元运算符! typeof ++ -- + -
  4. 幂运算**(右结合)
  5. 乘除取余* / %
  6. 加减+ -
  7. 比较< > <= >=
  8. 相等== != === !==
  9. 逻辑与&&
  10. 逻辑或||
  11. 条件? :
  12. 赋值= += 等(右结合)

口诀(中文)

"括号成员一元幂,乘除加减比较等,逻辑与或条件赋"

实用原则

不需要记住所有优先级,记住常用的就够了:

  • 乘除优于加减
  • 比较优于逻辑
  • && 优于 ||
  • 赋值最后
  • 不确定的地方用括号

常见错误

错误 1:忘记运算符优先级

javascript
// 错误
if (type === "user" || "admin") {
  // 总是 true!
  // ...
}

// 正确
if (type === "user" || type === "admin") {
  // ...
}

错误 2:前置/后置递增混淆

javascript
let i = 5;

// 容易混淆
let a = i++ * 2; // a = 10, i = 6
let b = ++i * 2; // b = 14, i = 7

// 建议单独写
i++;
let a = i * 2; // 更清晰

错误 3:链式比较

javascript
// JavaScript 不支持链式比较
let x = 5;
console.log(1 < x < 10); // true,但不是你想的那样!

// 实际执行:
// 1 < x => 1 < 5 => true
// true < 10 => 1 < 10 => true

// 正确做法
console.log(1 < x && x < 10); // true

总结

运算符优先级是 JavaScript 表达式求值的基础,理解它能避免很多隐蔽的 bug。

本节要点回顾

  • 优先级决定了不同运算符的执行顺序
  • 结合性决定了相同优先级运算符的执行顺序
  • 大部分运算符是左结合,赋值、条件、幂运算是右结合
  • 括号 () 的优先级最高,可以改变执行顺序
  • 常用优先级:乘除 > 加减 > 比较 > 逻辑与 > 逻辑或 > 赋值
  • 为了代码可读性,建议使用括号明确意图
  • 避免编写过于复杂的表达式
  • 不确定时,查文档或使用括号