第46课:运算符与表达式
运算符是 JavaScript 中执行计算、比较、赋值和逻辑判断的符号。表达式是由运算符和操作数组成的代码片段,计算后会产出一个值。理解每一种运算符的语法、返回值、副作用以及它们之间的优先级和结合性,是写出正确且可预测的 JavaScript 代码的前提。本节课将逐一拆解七大类别运算符的核心行为、隐式类型转换规则以及常见易错点,并通过综合表达式实战来巩固运算符优先级的心智模型。
1. 算术运算符:数值计算与 + 的特殊性
算术运算符接受数值操作数,返回一个数值。除加法外,其余运算符仅用于数值运算。
1.1 六种基础算术运算符
| 运算符 | 名称 | 示例 | 结果 | 说明 |
|---|---|---|---|---|
+ |
加法 | 5 + 3 |
8 |
若任一操作数是字符串,则执行字符串拼接(见下文 1.2)。 |
- |
减法 | 5 - 3 |
2 |
非数值操作数会先被转为数值。 |
* |
乘法 | 5 * 3 |
15 |
同上。 |
/ |
除法 | 6 / 3 |
2 |
同上。 |
% |
取余 | 7 % 3 |
1 |
返回除法后的余数,符号与被除数相同。 |
** |
指数 | 2 ** 3 |
8 |
ES2016 引入。等价于 Math.pow(2, 3)。右结合。 |
1 | console.log(7 % 3); // 1 |
1.2 加法运算符 + 的字符串拼接行为
+ 是唯一一个重载的算术运算符:只要有一个操作数是字符串,另一个操作数就会被转为字符串,然后执行拼接。
1 | console.log(5 + 3); // 8(数值加法) |
从左到右的求值顺序:
1 | console.log(1 + 2 + '3'); // '33' —— 先 1+2=3(数值),再 3+'3'='33' |
1.3 一元 + 和 - 运算符
一元 + 尝试将操作数转为数值,相当于 Number(value)。一元 - 在转换后取负。
1 | console.log(+'42'); // 42 |
实用技巧:+dateString 是将日期字符串快速转为时间戳的简便写法,但现代代码推荐使用 Date.parse() 或 new Date() 以获得更好的可读性。
1 | const timestamp = +new Date(); // 等价于 Date.now() |
2. 赋值运算符:值存储与复合赋值
2.1 基础赋值运算符 =
= 将右侧表达式的值赋给左侧变量。赋值表达式本身有返回值——即被赋予的值。
1 | let a; |
这一特性使得链式赋值成为可能:
1 | let x, y, z; |
2.2 复合赋值运算符
复合赋值运算符对变量执行运算后将结果赋回该变量。它们本质上是“先运算,再赋值”的语法糖。
| 运算符 | 等价于 | 运算符 | 等价于 |
|---|---|---|---|
+= |
x = x + y |
-= |
x = x - y |
*= |
x = x * y |
/= |
x = x / y |
%= |
x = x % y |
**= |
x = x ** y |
<<= |
x = x << y |
>>= |
x = x >> y |
>>>= |
x = x >>> y |
&= |
x = x & y |
| ` | =` | `x = x | y` |
&&= |
x && (x = y) |
||= |
x || (x = y) |
??= |
x ?? (x = y) |
1 | let num = 10; |
3. 比较运算符:== vs === 与字典序
比较运算符返回布尔值,是条件判断和控制流的基础。
3.1 严格相等 === 与宽松相等 ==
| 运算符 | 名称 | 行为 |
|---|---|---|
=== |
严格相等 | 类型不同直接返回 false;类型相同且值相同返回 true。 |
!== |
严格不等 | === 的取反。 |
== |
宽松相等 | 类型不同时强制转换类型后再比较(规则复杂,见第 44 课 5.2)。 |
!= |
宽松不等 | == 的取反。 |
实践原则:永远使用 === 和 !==,除非你明确需要 null == undefined 这种特殊行为。
特殊比较陷阱:
1 | console.log(NaN === NaN); // false —— NaN 是唯一不等于自身的值 |
3.2 大小比较与字典序
关系运算符 <、>、<=、>= 的比较规则:
- 两个字符串比较 → 字典序(逐字符 Unicode 码点)。
- 其他情况 → 尝试将两个操作数转为数值比较。
- 任一操作数转为
NaN→ 结果始终为false。
1 | console.log('apple' < 'banana'); // true —— 字典序:'a'(97) < 'b'(98) |
4. 逻辑运算符:短路求值与返回值
逻辑运算符 &&、|| 和 ?? 不仅返回布尔值,还可以返回其中一个操作数的原始值。这种特性常用于默认值赋值和条件执行。
4.1 ||(逻辑或):返回第一个 Truthy 值
expr1 || expr2 的行为:
- 若
expr1为 truthy,返回expr1(短路,不计算expr2)。 - 若
expr1为 falsy,返回expr2(无论expr2是 truthy 还是 falsy)。
1 | console.log(0 || 'default'); // 'default' —— 0 是 falsy |
经典用法:为变量提供默认值(但当 0、'' 是有效值时会有问题,此时应用 ??)。
4.2 &&(逻辑与):返回第一个 Falsy 值
expr1 && expr2 的行为:
- 若
expr1为 falsy,返回expr1(短路)。 - 若
expr1为 truthy,返回expr2。
1 | console.log(0 && 'Hello'); // 0 —— 第一个就是 falsy |
经典用法:条件执行(如 isLoggedIn && showDashboard())。
4.3 ??(空值合并运算符,ES2020)
expr1 ?? expr2:仅当 expr1 为 null 或 undefined 时返回 expr2,否则返回 expr1。这与 || 的关键区别在于它不视 0、''、false 为假值。
1 | console.log(0 ?? 'default'); // 0 —— 0 不是 null/undefined |
| 表达式 | || 结果 | ?? 结果 |
| ——————– | ———– | ———– |
| 0 || 'default' | 'default' | 0 |
| '' || 'default' | 'default' | '' |
| false || 'default' | 'default' | false |
| null ?? 'default' | 'default' | 'default' |
最佳实践:为变量提供默认值时优先使用 ??,除非你确定需要将所有 falsy 值都视为“无效”。
5. 位运算符:32 位整数操作
位运算符将操作数视为32 位有符号整数(BigInt 除外),在其二进制表示上逐位操作。
| 运算符 | 名称 | 示例 | 说明 |
|---|---|---|---|
& |
按位与 | 5 & 3 |
两位都为 1 则得 1。5(0101) & 3(0011) → 1(0001)。 |
| |
按位或 | 5 | 3 |
任一位为 1 则得 1。5(0101) | 3(0011) → 7(0111)。 |
^ |
按位异或 | 5 ^ 3 |
两位不同则得 1。5(0101) ^ 3(0011) → 6(0110)。 |
~ |
按位非 | ~5 |
翻转每一位。~5 → -6(补码表示)。 |
<< |
左移 | 5 << 1 |
各二进制位左移 1 位,右侧补 0。5(0101) → 10(1010)。 |
>> |
有符号右移 | 5 >> 1 |
各二进制位右移 1 位,左侧补符号位。5(0101) → 2(0010)。 |
>>> |
无符号右移 | -5 >>> 1 |
各二进制位右移 1 位,左侧补 0。结果总是正数。 |
应用场景:权限系统(用位掩码存储多个布尔状态)、颜色值操作、性能敏感的低级运算。
1 | const READ = 1; // 001 |
6. 其他重要运算符
6.1 三元条件运算符 ? :
JavaScript 唯一的三目运算符。condition ? exprIfTrue : exprIfFalse。
1 | const age = 20; |
注意嵌套:三元运算符可以嵌套,但会严重损害可读性。超过一层嵌套时请改用 if-else。
6.2 typeof 运算符
返回操作数的类型字符串。它是一个运算符,不是函数,因此括号是可选的(但常加括号以增强可读性)。
1 | console.log(typeof 42); // 'number' |
6.3 void 运算符
void 对任何表达式求值并**始终返回 undefined。其主要历史用途是在 <a href="javascript:void(0)"> 中阻止链接跳转,但现代代码应使用 <button> 或 event.preventDefault()**。
1 | console.log(void 0); // undefined |
6.4 逗号运算符 ,
逗号运算符从左到右求值每个表达式,并返回最后一个表达式的值。它主要用于在单行中执行多个操作(如 for 循环中声明多个变量)。
1 | const x = (1, 2, 3); // x = 3 |
注意:不要将逗号运算符与函数参数、数组字面量中的逗号分隔符混淆。
6.5 可选链 ?. 与空值合并 ?? 的组合
第 3 课(超链接)和第 45 课已介绍过可选链。这里强调它与 ?? 的经典组合:
1 | const user = null; |
7. 运算符优先级与结合性
当多个运算符出现在同一个表达式中时,优先级决定求值顺序,结合性决定同优先级运算符的求值方向。
7.1 快速参考:从高到低的优先级表(精选)
| 优先级 | 运算符类别 | 运算符示例 | 结合性 |
|---|---|---|---|
| 1 | 成员访问 / 计算属性 | obj.prop、obj[expr]、fn() |
左 |
| 2 | 一元运算符 | +、-、!、~、typeof、void、delete |
右 |
| 3 | 指数 | ** |
右 |
| 4 | 乘除取余 | *、/、% |
左 |
| 5 | 加减 | +、- |
左 |
| 6 | 位移 | <<、>>、>>> |
左 |
| 7 | 比较 | <、>、<=、>=、instanceof、in |
左 |
| 8 | 相等 | ==、!=、===、!== |
左 |
| 9 | 按位与 | & |
左 |
| 10 | 按位异或 | ^ |
左 |
| 11 | 按位或 | | |
左 |
| 12 | 逻辑与 | && |
左 |
| 13 | 逻辑或 / 空值合并 | ||、?? |
左 |
| 14 | 条件(三元) | ? : |
右 |
| 15 | 赋值 | =、+=、-= 等 |
右 |
| 16 | 逗号 | , |
左 |
7.2 结合性实例
右结合:指数 **、一元运算符、赋值、三元运算符。
1 | // 指数右结合 |
7.3 不确定时:加括号
括号 () 的优先级最高(实际上是分组运算符,不属于优先级表)。当对求值顺序有疑虑时,始终使用括号显式指定意图,不要依赖记忆优先级表。括号能消除歧义,提升代码可读性,避免因优先级理解错误导致的 Bug。
1 | // 意图模糊 |
课后练习
一、概念自测(选择题 / 填空题)
(单选)
5 + 3 + '2'的结果是?
A.10
B.'82'
C.'532'
D.'10'(单选)
0 ?? 'default'与0 || 'default'的结果分别是?
A.0和'default'
B.'default'和'default'
C.0和0
D.'default'和0(填空) 指数运算符
**的结合性是______(左/右)。(多选) 以下哪些运算符会进行短路求值(即第二个操作数可能不被计算)?
A.&&
B.||
C.??
D.+
二、AI 编程任务:编写面向 AI 的提示词
场景:你需要编写一个 JavaScript 工具函数 calculate(expression),能够对一个简单的字符串表达式进行求值。表达式仅包含两个正整数和四种运算符(+、-、*、/),格式为 "数字 运算符 数字"(如 "15 + 3")。要求如下:
- 使用
const声明函数,内部变量优先const。 - 使用字符串方法(如
split)解析操作数和运算符。 - 使用
switch语句根据运算符执行对应计算。 - 使用
Number()将字符串操作数转为数值。 - 处理除数为 0 的情况:若除数为 0,返回字符串
'Error: Division by zero'。 - 处理无效运算符:返回
'Error: Invalid operator'。 - 添加 JSDoc 注释说明参数、返回值和异常处理。
- 提供至少 5 个测试用例的调用示例(含除零和无效运算符)。
任务要求:请写出一段完整的中文提示词,发送给 AI,使其生成符合上述要求的 JavaScript 代码。提示词中需明确指定字符串解析方式、错误处理逻辑和代码风格。
课后练习答案
一、概念自测答案
B
- 解析:
5 + 3 = 8(数值加法),然后8 + '2' = '82'(字符串拼接)。
- 解析:
A
- 解析:
0 ?? 'default'→0(0不是null/undefined);0 || 'default'→'default'(0是 falsy)。
- 解析:
右
- 解析:
2 ** 3 ** 2先算3 ** 2 = 9,再算2 ** 9 = 512。
- 解析:
A、B、C
- 解析:
&&、||、??都会在第一个操作数确定结果时短路,不计算第二个操作数。+总是计算两个操作数。
- 解析:
二、AI 编程任务参考答案(提示词示例)
示例提示词:
“请编写一个 JavaScript 函数calculate(expression),接收一个字符串表达式,如'15 + 3',计算并返回结果。要求:
- 使用
const声明函数,内部变量使用const。- 使用
expression.split(' ')按空格拆分为[operand1, operator, operand2]。- 使用
Number()将操作数字符串转为数值。- 使用
switch (operator)匹配'+'、'-'、'*'、'/'四种情况。除法时检查除数num2 === 0,若为 0 返回'Error: Division by zero'。default分支返回'Error: Invalid operator'。- 添加 JSDoc 注释,说明
@param {string} expression、@returns {number|string}以及错误情况。- 在注释中提供测试用例:
calculate('10 + 5') // 15、calculate('8 * 3') // 24、calculate('10 / 0') // 'Error: Division by zero'、calculate('5 ^ 2') // 'Error: Invalid operator'、calculate('100 - 37') // 63。- 代码简洁,遵循 Airbnb 风格。直接输出完整代码。”