WinddSnow

JavaScript-Control-Flow-Conditionals-and-Loops

字数统计: 2.9k阅读时长: 11 min
2026/07/30

第47课:流程控制——条件语句与循环

程序默认按从上到下的顺序执行每一行代码。控制流(Control Flow)打破了这种线性执行,允许程序根据不同条件选择性地执行代码块,或重复执行某段逻辑。JavaScript 提供了两类核心控制流工具:条件语句if-elseswitch、三元运算符)和循环语句forwhiledo-while)。掌握它们的语法细节、适用场景和性能特性,是编写任何非平凡程序的必备能力。


1. if-else 条件分支

if 语句根据布尔表达式的结果来决定执行哪个代码块。

1.1 基本语法

1
2
3
4
5
6
7
if (condition) {
// condition 为 truthy 时执行
} else if (anotherCondition) {
// 上一个 if 为 falsy 且 anotherCondition 为 truthy 时执行
} else {
// 以上所有条件均为 falsy 时执行
}

关键细节

  • 条件表达式会被自动转换为布尔值(遵循第 44 课的 falsy/truthy 规则)。
  • 每个分支的代码块用 {} 包裹。即使只有一行语句,也必须加花括号——省略花括号会导致只有紧接着的第一条语句属于该分支,极易引发 Bug。
  • else if 实际上是 else 后紧跟一个 if 的简写,JavaScript 没有真正的 elseif 关键字。
1
2
3
4
5
6
7
8
9
10
11
const score = 85;

if (score >= 90) {
console.log('优秀');
} else if (score >= 80) {
console.log('良好'); // ← 执行这个分支
} else if (score >= 60) {
console.log('及格');
} else {
console.log('不及格');
}

1.2 常见陷阱:无花括号的 if

1
2
3
4
const isReady = false;
if (isReady)
console.log('准备就绪');
console.log('开始执行'); // ← 这行不在 if 内!始终会执行

永远不要省略花括号。这是 ESLint 的 curly 规则强制要求的原因。

1.3 条件判断中的常见模式

模式 代码示例 说明
提前返回(Guard Clause) if (!user) return; 在函数开头排除无效情况,减少嵌套。
范围判断 if (age >= 18 && age <= 65) 用逻辑运算符组合多个条件。
空值检查 if (data == null) 使用 == null 同时匹配 nullundefined
特征检测 if ('localStorage' in window) 检测浏览器是否支持某个 API。

2. switch 语句:多分支的清晰替代

当需要针对一个表达式的多个具体值进行分支判断时,switch 比连续的 if-else if 更清晰。

2.1 基本语法

1
2
3
4
5
6
7
8
9
10
switch (expression) {
case value1:
// 当 expression === value1 时执行
break;
case value2:
// 当 expression === value2 时执行
break;
default:
// 没有任何 case 匹配时执行
}

关键机制

  • switch 使用严格相等===)比较表达式的值与每个 case 值。
  • 一旦匹配到某个 case,会从该处开始执行后续所有语句,直到遇到 breakswitch 块结束。这称为穿透(Fall-through)
  • 如果没有任何 case 匹配,执行 default 分支(若存在)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const fruit = 'apple';

switch (fruit) {
case 'banana':
console.log('香蕉的价格是 5 元');
break;
case 'apple':
console.log('苹果的价格是 8 元');
break;
case 'orange':
console.log('橙子的价格是 6 元');
break;
default:
console.log('抱歉,没有这个水果');
}

2.2 利用穿透:多个 case 共享同一逻辑

当多个值应执行相同操作时,有意省略 break 实现共享:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const day = 3; // 周三

switch (day) {
case 1:
case 2:
case 3:
case 4:
case 5:
console.log('工作日');
break;
case 6:
case 7:
console.log('周末');
break;
default:
console.log('无效的日期');
}

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
2
3
const age = 20;
const beverage = age >= 18 ? '啤酒' : '果汁';
console.log(beverage); // '啤酒'

三元与 if-else 的关键区别

  • if-else语句,不返回值,只能控制执行流。
  • ? :表达式,本身产出一个值,可用于赋值、return、模板字符串等场景。

嵌套三元:可以嵌套,但会迅速降低可读性。超过一层嵌套时,改用 if-else 或函数抽离。

1
2
3
4
5
6
7
8
9
10
// ❌ 难以理解
const message = score > 90 ? '优秀' : score > 60 ? '及格' : '不及格';

// ✅ 清晰
function getGrade(score) {
if (score > 90) return '优秀';
if (score > 60) return '及格';
return '不及格';
}
const message = getGrade(score);

4. 循环语句:重复执行代码

循环允许你重复执行一段代码,直到某个条件变为 false。JavaScript 提供四种循环语法:forwhiledo-whilefor-of/for-in(后两者将在第 52 课讨论)。

4.1 for 循环:最常用的循环

1
2
3
for (初始化; 条件; 迭代后操作) {
// 循环体
}

执行顺序:初始化 → 条件检查(若 true)→ 循环体 → 迭代后操作 → 条件检查 → …

1
2
3
for (let i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}

三个表达式均可省略(但分号必须保留):

  • 省略初始化:循环变量已在外部声明。
  • 省略条件:视为 true,成为无限循环
  • 省略迭代后操作:在循环体内部手动更新循环变量。
1
2
3
4
5
let i = 0;
for (; i < 5; ) {
console.log(i);
i++;
}

最佳实践:始终使用 let 声明循环变量,使其作用域限制在循环块内。

4.2 while 循环:前置条件检查

1
2
3
while (condition) {
// 条件为 truthy 时重复执行
}

适用于事先不确定循环次数的场景,如读取数据流、等待用户输入。

1
2
3
4
5
let attempts = 3;
while (attempts > 0) {
console.log(`剩余尝试次数:${attempts}`);
attempts--;
}

4.3 do-while 循环:后置条件检查

1
2
3
do {
// 至少执行一次
} while (condition);

do-whilewhile 的唯一区别是:循环体至少执行一次,即使条件一开始就是 falsy。适用于“先执行再询问”的场景,如显示菜单后等待用户选择。

1
2
3
4
let input;
do {
input = prompt('请输入一个正数:');
} while (Number(input) <= 0);

prompt 是浏览器 API,Node.js 环境下不存在。此处仅为演示 do-while 的行为模式。

4.4 循环控制:breakcontinue

语句 行为 影响范围
break 立即终止整个循环,执行循环后的下一条语句。 当前所在的最内层循环。
continue 跳过本次迭代的剩余代码,进入下一次迭代检查。 当前所在的最内层循环。
1
2
3
4
5
for (let i = 0; i < 10; i++) {
if (i === 5) break; // 当 i=5 时终止循环
if (i % 2 === 0) continue; // 跳过偶数,不执行后面的 console.log
console.log(i); // 输出 1, 3
}

标签语句:可以为循环命名,结合 break labelName 跳出指定的外层循环(极少使用,但面试偶尔考察)。

1
2
3
4
5
6
7
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) break outer; // 跳出外层循环
console.log(i, j);
}
}
// 输出:(0,0) (0,1) (0,2) (1,0) 之后跳出

5. 性能考量与最佳实践

5.1 循环优化基础

  • 缓存数组长度:在 for 循环中预先获取 arr.length,避免每次迭代都访问一次 length 属性(现代 JS 引擎对这类模式已有优化,但仍属好习惯)。
  • 避免在循环内操作 DOM:将计算结果累积,循环结束后一次性更新 DOM。每次 DOM 操作都可能触发回流。
  • break 提前退出:若在有序数据中查找目标值,一旦找到就 break,不要继续遍历后续元素。

5.2 函数式替代

现代 JavaScript 中,许多循环可以用数组的高阶方法替代(forEachmapfilterreduce),它们更声明式,能减少 break/continue 的误用。这些方法将在第 52 课详述,此处仅展示对比:

1
2
3
4
5
6
7
8
// 命令式循环
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
doubled.push(numbers[i] * 2);
}

// 声明式 map(语义更清晰)
const doubled = numbers.map(n => n * 2);

原则:当逻辑涉及 breakcontinue 或需要提前退出时,for 循环更合适;当需要“对每个元素做某件事并生成新数组”时,优先使用 map/filter/forEach


课后练习

一、概念自测(选择题 / 填空题)

  1. (单选) switch 语句使用什么运算符来比较表达式的值和 case 值?
    A. ==
    B. ===
    C. Object.is
    D. equals

  2. (单选) 以下关于 do-while 循环的描述,正确的是?
    A. 循环体可能一次都不执行。
    B. 条件检查在循环体执行之前。
    C. 循环体至少执行一次。
    D. 它不能使用 break 退出。

  3. (填空)for 循环中,要跳过当前迭代的剩余代码并进入下一次迭代,应使用 ______ 语句。

  4. (多选) 以下哪些是 JavaScript 中合法的循环语句?
    A. for
    B. while
    C. loop
    D. do-while

二、AI 编程任务:编写面向 AI 的提示词

场景:你需要编写一个 JavaScript 函数 fizzBuzz(n),它接收一个正整数 n,返回一个从 1n 的字符串数组,要求如下:

  • 如果数字能被 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),以及循环和条件语句的使用方式。


课后练习答案

一、概念自测答案

  1. B

    • 解析:switch 使用严格相等(===)进行匹配。
  2. C

    • 解析:do-while 先执行循环体,再检查条件,因此循环体至少执行一次。
  3. continue

    • 解析:continue 跳过本次迭代剩余的语句,进入下一次条件检查。break 是完全终止循环。
  4. A、B、D

    • 解析:JavaScript 的循环语句包括 forwhiledo-while,以及 for-in/for-ofloop 不是 JavaScript 的关键字。

二、AI 编程任务参考答案(提示词示例)

示例提示词
“请编写一个 JavaScript 函数 fizzBuzz(n),接收正整数 n,返回从 1n 的字符串数组。要求:

  • 使用 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) 并注释输出的前几个元素。
  • 代码简洁,直接输出完整代码。”
CATALOG
  1. 1. 第47课:流程控制——条件语句与循环
    1. 1.1. 1. if-else 条件分支
      1. 1.1.1. 1.1 基本语法
      2. 1.1.2. 1.2 常见陷阱:无花括号的 if
      3. 1.1.3. 1.3 条件判断中的常见模式
    2. 1.2. 2. switch 语句:多分支的清晰替代
      1. 1.2.1. 2.1 基本语法
      2. 1.2.2. 2.2 利用穿透:多个 case 共享同一逻辑
      3. 1.2.3. 2.3 switch vs if-else if 的选择
    3. 1.3. 3. 三元条件运算符 ? :
    4. 1.4. 4. 循环语句:重复执行代码
      1. 1.4.1. 4.1 for 循环:最常用的循环
      2. 1.4.2. 4.2 while 循环:前置条件检查
      3. 1.4.3. 4.3 do-while 循环:后置条件检查
      4. 1.4.4. 4.4 循环控制:break 与 continue
    5. 1.5. 5. 性能考量与最佳实践
      1. 1.5.1. 5.1 循环优化基础
      2. 1.5.2. 5.2 函数式替代
    6. 1.6. 课后练习
      1. 1.6.1. 一、概念自测(选择题 / 填空题)
      2. 1.6.2. 二、AI 编程任务:编写面向 AI 的提示词
    7. 1.7. 课后练习答案
      1. 1.7.1. 一、概念自测答案
      2. 1.7.2. 二、AI 编程任务参考答案(提示词示例)