第47课:流程控制——条件语句与循环
程序默认按从上到下的顺序执行每一行代码。控制流(Control Flow)打破了这种线性执行,允许程序根据不同条件选择性地执行代码块,或重复执行某段逻辑。JavaScript 提供了两类核心控制流工具:条件语句(if-else、switch、三元运算符)和循环语句(for、while、do-while)。掌握它们的语法细节、适用场景和性能特性,是编写任何非平凡程序的必备能力。
1. if-else 条件分支
if 语句根据布尔表达式的结果来决定执行哪个代码块。
1.1 基本语法
1 | if (condition) { |
关键细节:
- 条件表达式会被自动转换为布尔值(遵循第 44 课的 falsy/truthy 规则)。
- 每个分支的代码块用
{}包裹。即使只有一行语句,也必须加花括号——省略花括号会导致只有紧接着的第一条语句属于该分支,极易引发 Bug。 else if实际上是else后紧跟一个if的简写,JavaScript 没有真正的elseif关键字。
1 | const score = 85; |
1.2 常见陷阱:无花括号的 if
1 | const isReady = false; |
永远不要省略花括号。这是 ESLint 的 curly 规则强制要求的原因。
1.3 条件判断中的常见模式
| 模式 | 代码示例 | 说明 |
|---|---|---|
| 提前返回(Guard Clause) | if (!user) return; |
在函数开头排除无效情况,减少嵌套。 |
| 范围判断 | if (age >= 18 && age <= 65) |
用逻辑运算符组合多个条件。 |
| 空值检查 | if (data == null) |
使用 == null 同时匹配 null 和 undefined。 |
| 特征检测 | if ('localStorage' in window) |
检测浏览器是否支持某个 API。 |
2. switch 语句:多分支的清晰替代
当需要针对一个表达式的多个具体值进行分支判断时,switch 比连续的 if-else if 更清晰。
2.1 基本语法
1 | switch (expression) { |
关键机制:
switch使用严格相等(===)比较表达式的值与每个case值。- 一旦匹配到某个
case,会从该处开始执行后续所有语句,直到遇到break或switch块结束。这称为穿透(Fall-through)。 - 如果没有任何
case匹配,执行default分支(若存在)。
1 | const fruit = 'apple'; |
2.2 利用穿透:多个 case 共享同一逻辑
当多个值应执行相同操作时,有意省略 break 实现共享:
1 | const day = 3; // 周三 |
2.3 switch vs if-else if 的选择
| 场景 | 推荐 | 理由 |
|---|---|---|
| 判断 3 个以上离散值 | switch |
结构更清晰,意图更明显。 |
| 判断复杂条件(范围、逻辑组合) | if-else |
switch 只能做严格相等比较,范围判断必须用 if-else。 |
| 仅 1-2 个分支 | if-else |
switch 语法过于啰嗦。 |
| 需要穿透共享逻辑 | switch |
这是 switch 独有的能力。 |
3. 三元条件运算符 ? :
三元运算符是 if-else 的表达式形式,它产出一个值,因此可以嵌入到更大的表达式中。
1 | const age = 20; |
三元与 if-else 的关键区别:
if-else是语句,不返回值,只能控制执行流。? :是表达式,本身产出一个值,可用于赋值、return、模板字符串等场景。
嵌套三元:可以嵌套,但会迅速降低可读性。超过一层嵌套时,改用 if-else 或函数抽离。
1 | // ❌ 难以理解 |
4. 循环语句:重复执行代码
循环允许你重复执行一段代码,直到某个条件变为 false。JavaScript 提供四种循环语法:for、while、do-while 和 for-of/for-in(后两者将在第 52 课讨论)。
4.1 for 循环:最常用的循环
1 | for (初始化; 条件; 迭代后操作) { |
执行顺序:初始化 → 条件检查(若 true)→ 循环体 → 迭代后操作 → 条件检查 → …
1 | for (let i = 0; i < 5; i++) { |
三个表达式均可省略(但分号必须保留):
- 省略初始化:循环变量已在外部声明。
- 省略条件:视为
true,成为无限循环。 - 省略迭代后操作:在循环体内部手动更新循环变量。
1 | let i = 0; |
最佳实践:始终使用 let 声明循环变量,使其作用域限制在循环块内。
4.2 while 循环:前置条件检查
1 | while (condition) { |
适用于事先不确定循环次数的场景,如读取数据流、等待用户输入。
1 | let attempts = 3; |
4.3 do-while 循环:后置条件检查
1 | do { |
do-while 与 while 的唯一区别是:循环体至少执行一次,即使条件一开始就是 falsy。适用于“先执行再询问”的场景,如显示菜单后等待用户选择。
1 | let input; |
prompt 是浏览器 API,Node.js 环境下不存在。此处仅为演示 do-while 的行为模式。
4.4 循环控制:break 与 continue
| 语句 | 行为 | 影响范围 |
|---|---|---|
break |
立即终止整个循环,执行循环后的下一条语句。 | 当前所在的最内层循环。 |
continue |
跳过本次迭代的剩余代码,进入下一次迭代检查。 | 当前所在的最内层循环。 |
1 | for (let i = 0; i < 10; i++) { |
标签语句:可以为循环命名,结合 break labelName 跳出指定的外层循环(极少使用,但面试偶尔考察)。
1 | outer: for (let i = 0; i < 3; i++) { |
5. 性能考量与最佳实践
5.1 循环优化基础
- 缓存数组长度:在
for循环中预先获取arr.length,避免每次迭代都访问一次length属性(现代 JS 引擎对这类模式已有优化,但仍属好习惯)。 - 避免在循环内操作 DOM:将计算结果累积,循环结束后一次性更新 DOM。每次 DOM 操作都可能触发回流。
- 用
break提前退出:若在有序数据中查找目标值,一旦找到就break,不要继续遍历后续元素。
5.2 函数式替代
现代 JavaScript 中,许多循环可以用数组的高阶方法替代(forEach、map、filter、reduce),它们更声明式,能减少 break/continue 的误用。这些方法将在第 52 课详述,此处仅展示对比:
1 | // 命令式循环 |
原则:当逻辑涉及 break、continue 或需要提前退出时,for 循环更合适;当需要“对每个元素做某件事并生成新数组”时,优先使用 map/filter/forEach。
课后练习
一、概念自测(选择题 / 填空题)
(单选)
switch语句使用什么运算符来比较表达式的值和case值?
A.==
B.===
C.Object.is
D.equals(单选) 以下关于
do-while循环的描述,正确的是?
A. 循环体可能一次都不执行。
B. 条件检查在循环体执行之前。
C. 循环体至少执行一次。
D. 它不能使用break退出。(填空) 在
for循环中,要跳过当前迭代的剩余代码并进入下一次迭代,应使用______语句。(多选) 以下哪些是 JavaScript 中合法的循环语句?
A.for
B.while
C.loop
D.do-while
二、AI 编程任务:编写面向 AI 的提示词
场景:你需要编写一个 JavaScript 函数 fizzBuzz(n),它接收一个正整数 n,返回一个从 1 到 n 的字符串数组,要求如下:
- 如果数字能被 3 整除,用
'Fizz'代替。 - 如果数字能被 5 整除,用
'Buzz'代替。 - 如果数字能同时被 3 和 5 整除,用
'FizzBuzz'代替。 - 其他数字直接转为字符串(如
'7')。 - 使用
for循环实现。内部用if-else判断条件,注意'FizzBuzz'的判断必须先于单个条件(否则会被'Fizz'或'Buzz'拦截)。 - 使用
const声明函数,循环变量用let。 - 添加 JSDoc 注释说明参数和返回值。
任务要求:请写出一段完整的中文提示词,发送给 AI,使其生成符合上述要求的 JavaScript 函数。提示词中需明确指定条件判断的顺序(先判断 i % 3 === 0 && i % 5 === 0),以及循环和条件语句的使用方式。
课后练习答案
一、概念自测答案
B
- 解析:
switch使用严格相等(===)进行匹配。
- 解析:
C
- 解析:
do-while先执行循环体,再检查条件,因此循环体至少执行一次。
- 解析:
continue- 解析:
continue跳过本次迭代剩余的语句,进入下一次条件检查。break是完全终止循环。
- 解析:
A、B、D
- 解析:JavaScript 的循环语句包括
for、while、do-while,以及for-in/for-of。loop不是 JavaScript 的关键字。
- 解析:JavaScript 的循环语句包括
二、AI 编程任务参考答案(提示词示例)
示例提示词:
“请编写一个 JavaScript 函数fizzBuzz(n),接收正整数n,返回从1到n的字符串数组。要求:
- 使用
const声明函数,循环变量用let,结果数组用const result = []。- 使用
for (let i = 1; i <= n; i++)遍历。- 在循环内使用
if-else判断:
- 先判断
i % 3 === 0 && i % 5 === 0,成立则result.push('FizzBuzz')。- 再判断
i % 3 === 0,成立则result.push('Fizz')。- 再判断
i % 5 === 0,成立则result.push('Buzz')。- 否则
result.push(String(i))。- 函数返回
result。- 添加 JSDoc 注释:
@param {number} n、@returns {string[]}。- 提供使用示例
fizzBuzz(15)并注释输出的前几个元素。- 代码简洁,直接输出完整代码。”