------分支判断 · 三大循环 · 跳转控制 · 实战案例 · 性能优化全解析
知识定位: JavaScript 流程控制 → 条件分支 → 循环结构 → 跳转语句
适合人群: 前端初学者、正在系统学习 JS 的同学
涵盖内容: if/else · switch · while · do-while · for · 嵌套循环 · break · continue · 九九乘法表四种形式 · 性能优化 · 面试高频考点
内容规模: 24 章 · 80+ 知识点 · 19 个完整可运行示例 · 9 道经典练习题
目录
- 回顾:分支语句总结
- [关键回顾:switch 的穿透(fall-through)现象](#关键回顾:switch 的穿透(fall-through)现象)
- 名词解释与术语表
- [while 循环](#while 循环)
- [3.1 语法结构](#3.1 语法结构)
- [3.2 执行流程](#3.2 执行流程)
- [3.3 实现「可结束循环」的两个必要条件](#3.3 实现「可结束循环」的两个必要条件)
- [3.4 课堂案例](#3.4 课堂案例)
- [3.5 三要素记忆口诀](#3.5 三要素记忆口诀)
- [do-while 循环](#do-while 循环)
- [4.1 语法结构](#4.1 语法结构)
- [4.2 执行流程](#4.2 执行流程)
- [4.3 课堂案例](#4.3 课堂案例)
- [while 与 do-while 的本质区别](#while 与 do-while 的本质区别)
- [5.1 概念对比](#5.1 概念对比)
- [5.2 关键区别演示](#5.2 关键区别演示)
- [5.3 双循环流程对比(Mermaid)](#5.3 双循环流程对比(Mermaid))
- [5.4 实际应用场景对比](#5.4 实际应用场景对比)
- [for 循环](#for 循环)
- [6.1 语法结构](#6.1 语法结构)
- [6.2 四个部分的详细解析](#6.2 四个部分的详细解析)
- [6.3 执行流程](#6.3 执行流程)
- [6.4 课堂案例](#6.4 课堂案例)
- [6.5 for 循环执行步骤逐帧分析](#6.5 for 循环执行步骤逐帧分析)
- [for 循环的特殊结构](#for 循环的特殊结构)
- [7.1 特殊结构总结表](#7.1 特殊结构总结表)
- 不需要标记变量的循环
- [8.1 完整案例](#8.1 完整案例)
- [8.2 实际应用场景](#8.2 实际应用场景)
- 循环嵌套
- [9.1 概念](#9.1 概念)
- [9.2 执行顺序可视化(Mermaid)](#9.2 执行顺序可视化(Mermaid))
- [9.3 动态表格案例](#9.3 动态表格案例)
- 九九乘法表四种形式
- [10.1 四种形式的规律总结](#10.1 四种形式的规律总结)
- [10.2 第一种:左下三角](#10.2 第一种:左下三角)
- [10.3 第二种:左上三角](#10.3 第二种:左上三角)
- [10.4 第三种:右下三角](#10.4 第三种:右下三角)
- [10.5 第四种:右上三角](#10.5 第四种:右上三角)
- [10.6 知识点提炼](#10.6 知识点提炼)
- [跳转语句:break 与 continue](#跳转语句:break 与 continue)
- [11.1 break 语句](#11.1 break 语句)
- [11.2 continue 语句](#11.2 continue 语句)
- [11.3 完整对比案例](#11.3 完整对比案例)
- [11.4 break 配合 while(true) 取随机数](#11.4 break 配合 while(true) 取随机数)
- [11.5 break 与 continue 总结](#11.5 break 与 continue 总结)
- 省略大括号的写法
- 分支作业案例讲解
- [13.1 案例一:字符串拼接](#13.1 案例一:字符串拼接)
- [13.2 案例二:阶梯水价](#13.2 案例二:阶梯水价)
- [13.3 案例三:体重判断](#13.3 案例三:体重判断)
- [13.4 案例四:闰年判断](#13.4 案例四:闰年判断)
- 强化练习题完整解答
- [练习一:while 循环写四种形式的九九乘法表](#练习一:while 循环写四种形式的九九乘法表)
- [练习二:100-200 之间能被 3 或 7 整除的数](#练习二:100-200 之间能被 3 或 7 整除的数)
- [练习三:三位数中各位上有 3 或 7 的数](#练习三:三位数中各位上有 3 或 7 的数)
- [练习四:100 的阶乘](#练习四:100 的阶乘)
- [练习五:1! + 2! + ... + 20!](#练习五:1! + 2! + … + 20!)
- 练习六:水仙花数
- [练习七:100-200 之间所有的素数](#练习七:100-200 之间所有的素数)
- 练习八:等腰三角形
- 练习九:格式化数字输出(01-50,每行10个)
- 默写题目与答案解析
- 题目一:程序输出结果分析
- [题目二:switch 穿透输出](#题目二:switch 穿透输出)
- 三种循环横向对比总结
- [16.1 语法对比](#16.1 语法对比)
- [16.2 选择指南](#16.2 选择指南)
- [16.3 三种循环等价转换](#16.3 三种循环等价转换)
- 经典场景:循环在真实项目中的应用
- [17.1 电商网站:商品列表渲染](#17.1 电商网站:商品列表渲染)
- [17.2 倒计时功能(循环 + 定时器)](#17.2 倒计时功能(循环 + 定时器))
- [17.3 表单验证(循环检查多个输入)](#17.3 表单验证(循环检查多个输入))
- [17.4 数据可视化:动态图表生成](#17.4 数据可视化:动态图表生成)
- [17.5 分页功能(循环生成页码)](#17.5 分页功能(循环生成页码))
- [17.6 搜索过滤功能(循环遍历过滤)](#17.6 搜索过滤功能(循环遍历过滤))
- 循环性能优化建议(2026年最新研究)
- [18.1 性能优化优先级排序](#18.1 性能优化优先级排序)
- [18.2 关键优化详解](#18.2 关键优化详解)
- [18.3 V8 引擎已自动优化的"伪优化"](#18.3 V8 引擎已自动优化的"伪优化")
- [18.4 时间复杂度与实际性能](#18.4 时间复杂度与实际性能)
- [18.5 现代优化总结(2026版)](#18.5 现代优化总结(2026版))
- 常见错误与避坑指南
- [19.1 死循环](#19.1 死循环)
- [19.2 off-by-one 错误(差一错误)](#19.2 off-by-one 错误(差一错误))
- [19.3 省略大括号导致逻辑错误](#19.3 省略大括号导致逻辑错误)
- [19.4 变量作用域陷阱(var 声明)](#19.4 变量作用域陷阱(var 声明))
- [19.5 常见错误总结表](#19.5 常见错误总结表)
- 循环进阶知识(扩展阅读)
- [20.1 循环标签(Label)与多层跳出](#20.1 循环标签(Label)与多层跳出)
- [20.2 循环与闭包(经典陷阱)](#20.2 循环与闭包(经典陷阱))
- [20.3 循环与异步(Promise 在循环中的使用)](#20.3 循环与异步(Promise 在循环中的使用))
- [20.4 循环在不同 JavaScript 引擎中的差异](#20.4 循环在不同 JavaScript 引擎中的差异)
- 知识点专业总结与对比
- [21.1 三种循环的本质区别](#21.1 三种循环的本质区别)
- [21.2 循环选择决策树](#21.2 循环选择决策树)
- [21.3 性能对比总结(2026 实测数据)](#21.3 性能对比总结(2026 实测数据))
- [21.4 循环与函数式编程对比](#21.4 循环与函数式编程对比)
- 真实项目案例分析
- [22.1 大型网站中的循环应用](#22.1 大型网站中的循环应用)
- 面试高频考点
- [23.1 循环相关面试题](#23.1 循环相关面试题)
- [23.2 算法题中的循环应用](#23.2 算法题中的循环应用)
- 参考资源与延伸阅读
- [24.1 官方文档](#24.1 官方文档)
- [24.2 性能研究(2026 最新)](#24.2 性能研究(2026 最新))
- [24.3 最佳实践资源](#24.3 最佳实践资源)
- [24.4 在线练习平台](#24.4 在线练习平台)
- [24.5 推荐书籍](#24.5 推荐书籍)
- [24.6 视频课程](#24.6 视频课程)
1 回顾:分支语句总结
在上一节课中,我们系统学习了 JavaScript 的五种分支结构:
| 编号 | 结构名称 | 典型写法 | 适用场景 |
|---|---|---|---|
| ① | 单向分支 | if (条件) { } |
只需要在条件成立时执行某段代码 |
| ② | 双向分支 | if (条件) { } else { } |
两种情况择一执行 |
| ③ | 多向分支 (else if) | if ... else if ... else |
多个互斥区间判断 |
| ④ | 多向分支 (switch) | switch (表达式) { case: } |
离散值的多路匹配 |
| ⑤ | 嵌套分支 | if 内部再嵌套 if | 复合条件的多维判断 |
关键回顾:switch 的穿透(fall-through)现象
js
var a = 20;
switch (a) {
case 10: a += 10;
case 20: a += 20; // 匹配到这里,开始执行
case 30: a += 30; // 没有 break,继续穿透
default: a += 100; // 继续穿透
}
console.log(a); // 20 + 20 + 30 + 100 = 170
📝 代码解释:switch 穿透(fall-through)
逐步执行分析:
初始值:a = 20
① 检测 case 10:20 === 10? → false → 跳过
② 检测 case 20:20 === 20? → true → 开始执行
执行:a += 20 → a = 20 + 20 = 40
没有 break → 继续向下穿透!
③ 检测 case 30:不再判断,直接执行!(穿透)
执行:a += 30 → a = 40 + 30 = 70
没有 break → 继续穿透!
④ 到达 default:不再判断,直接执行!(穿透)
执行:a += 100 → a = 70 + 100 = 170
⑤ 输出:170
穿透规律:
js
// 有 break:匹配 case 后执行,遇到 break 退出
switch (x) {
case 1: doA(); break; // 执行 doA(),然后退出
case 2: doB(); break; // 如果不匹配,跳过
}
// 无 break:匹配 case 后,一路向下执行到结束
switch (x) {
case 1: doA(); // 匹配后,执行 doA()
case 2: doB(); // 没有 break,继续执行 doB()
case 3: doC(); // 继续执行 doC()
default: doD(); // 继续执行 doD()
}
穿透的实际利用(合法用法):
js
// 利用穿透:多个 case 共享同一段代码
switch (day) {
case 'Saturday':
case 'Sunday':
console.log('周末!'); // Saturday 和 Sunday 都会执行这行
break;
default:
console.log('工作日');
}
关键知识点:
| 特性 | 说明 | 注意事项 |
|---|---|---|
| 穿透 | 匹配 case 后,无 break 则继续向下执行 | 初学者最常见的 Bug |
| break 的必要性 | 用于结束当前 case,防止穿透 | 每个 case 一般都需要 break |
| 穿透的正当用途 | 多个 case 执行相同代码 | 合理利用可减少重复代码 |
| default | 没有匹配时执行 | 也会被穿透执行 |
穿透本质: switch 找到匹配的 case 后,如果没有
break,会一直执行到 switch 结束。这是初学者最常见的 Bug 来源之一。
2 名词解释与术语表
在正式进入循环语句之前,先建立概念体系,避免混淆。
| 术语 | 英文 | 解释 |
|---|---|---|
| 循环 | Loop | 让一段代码重复执行的结构 |
| 循环体 | Loop Body | 循环中被重复执行的语句块 |
| 循环条件 | Loop Condition | 决定是否继续循环的布尔表达式 |
| 循环标记变量 | Loop Counter / Iterator | 用于追踪循环状态的变量,通常叫 i、j、k |
| 死循环 | Infinite Loop | 条件永远为 true,永远不会结束的循环,会导致页面卡死 |
| 迭代 | Iteration | 每一次循环执行称为一次迭代 |
| 嵌套循环 | Nested Loop | 循环内部再包含循环 |
| 跳转语句 | Jump Statement | 改变程序正常执行流程的语句,如 break、continue |
| 穿透 | Fall-through | switch 中没有 break 时,继续执行下一个 case 的现象 |
| 前测循环 | Pre-test Loop | 先判断条件再执行循环体(while、for) |
| 后测循环 | Post-test Loop | 先执行循环体再判断条件(do-while) |
| 累加器 | Accumulator | 用于在循环中不断累加值的变量,通常初始化为 0 |
| 累乘器 | Multiplier | 用于在循环中不断累乘值的变量,通常初始化为 1 |
| 步长 | Step | 每次迭代循环标记变量的变化量 |
3 while 循环
3.1 语法结构
js
while (条件表达式) {
循环体语句;
}
3.2 执行流程(Mermaid 流程图)
true
false
开始
初始化循环变量
条件表达式?
执行循环体
更新循环变量
结束
3.3 实现「可结束循环」的两个必要条件
1. 循环条件不能永远成立(否则死循环)
2. 随着循环次数增加,循环条件应该越来越趋向于不成立
类比理解: 就像跑步记圈。条件是"圈数 < 10",每跑完一圈就
圈数++。如果忘记圈数++,那永远满足条件,就是死循环。
3.4 课堂案例:while 循环(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>while 循环示例</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.output { background: #1e1e1e; color: #d4d4d4; padding: 20px; border-radius: 8px; margin: 10px 0; }
.output span { display: block; padding: 2px 0; }
.highlight { color: #4FC3F7; }
h3 { color: #333; border-left: 4px solid #2196F3; padding-left: 10px; }
</style>
</head>
<body>
<h3>① 输出 7~15 之间的数字</h3>
<div class="output" id="out1"></div>
<h3>② 倒序输出 56~43</h3>
<div class="output" id="out2"></div>
<h3>③ 输出 10 以内所有偶数</h3>
<div class="output" id="out3"></div>
<h3>④ 计算 100 以内所有数字的和</h3>
<div class="output" id="out4"></div>
<h3>⑤ 计算 10 以内所有奇数的乘积</h3>
<div class="output" id="out5"></div>
<script>
// 工具函数:向指定容器输出内容
function print(id, text) {
var div = document.getElementById(id);
var span = document.createElement('span');
span.textContent = text;
div.appendChild(span);
}
// ① 循环输出数字 7~15
var i = 7; // 【初始化】设置循环起点
while (i <= 15) { // 【条件判断】i≤15 时继续循环
print('out1', i); // 【循环体】输出当前 i 的值
i++; // 【更新】i 自增 1,向结束条件靠近
}
print('out1', '循环结束后 i = ' + i); // 16(条件不满足时的值)
// ② 倒序输出数字 56~43
var i = 56; // 【初始化】从大到小,起点是 56
while (i >= 43) { // 【条件判断】i≥43 时继续(倒序条件)
print('out2', i); // 【循环体】输出当前值
i--; // 【更新】i 自减 1(倒序关键)
}
print('out2', '循环结束后 i = ' + i); // 42(最后一次不满足条件的值)
// ③ 输出 10 以内所有偶数(步长为 2)
var i = 0; // 【初始化】从 0 开始(第一个偶数)
while (i <= 10) { // 【条件判断】不超过 10
print('out3', i); // 【循环体】输出偶数
i += 2; // 【更新】步长为 2,跳过奇数
}
print('out3', '循环结束后 i = ' + i); // 12(10+2,超出范围)
// ④ 计算 100 以内所有数字的和(累加器 sum 初始值为 0)
var sum = 0; // 【累加器】初始化为 0(加法单位元)
var i = 1; // 【循环变量】从 1 开始
while (i <= 100) { // 【条件判断】累加到 100
sum += i; // 【累加操作】sum = sum + i
i++; // 【更新】i 递增
}
print('out4', '1+2+3+...+100 = ' + sum); // 5050
print('out4', '(这是著名的高斯求和,答案是 5050)');
// ⑤ 计算 10 以内所有奇数的乘积(累乘器 res 初始值为 1)
var res = 1; // 【累乘器】初始化为 1(乘法单位元)
var i = 1; // 【循环变量】从 1(第一个奇数)开始
while (i < 10) { // 【条件判断】小于 10
res *= i; // 【累乘操作】res = res * i
i += 2; // 【更新】步长为 2,遍历奇数
}
print('out5', '1×3×5×7×9 = ' + res); // 945
</script>
</body>
</html>

📝 代码说明与知识点提炼
案例① 正序输出(基础模式)
js
var i = 7; // 起点
while (i <= 15) { // 终点条件
// 循环体
i++; // 步长 +1
}
- 知识点: while 循环三要素(初始化、条件、更新)缺一不可
- 特性: 循环变量在循环结束后的值 = 第一个不满足条件的值(16)
- 应用: 遍历数字范围、生成序列
案例② 倒序输出(反向遍历)
js
var i = 56; // 从大数开始
while (i >= 43) { // 递减到小数
// 循环体
i--; // 步长 -1(关键)
}
- 知识点: 倒序的本质是"起点大、终点小、步长负"
- 特性: 条件运算符从
<=变为>=,更新从++变为-- - 应用: 倒计时、从后向前遍历
案例③ 步长控制(跳跃遍历)
js
var i = 0;
while (i <= 10) {
// 处理 i
i += 2; // 步长 +2
}
- 知识点: 步长不一定是 1,可以通过
i += n控制跳跃间隔 - 特性: 筛选特定规律的数字(偶数、3 的倍数等)
- 优化技巧: 直接跳跃比"循环+判断"更高效
案例④ 累加器模式(求和)
js
var sum = 0; // 累加器初始值 0(加法单位元)
var i = 1;
while (i <= 100) {
sum += i; // 累加
i++;
}
- 知识点: 累加器必须初始化为 0(加法的中性元素)
- 数学原理: 高斯求和公式
sum = n(n+1)/2,100 项求和 = 5050 - 特性: 需要两个变量(循环变量 i + 累加器 sum)
- 应用: 统计总数、计算平均值、数据聚合
案例⑤ 累乘器模式(求积)
js
var res = 1; // 累乘器初始值 1(乘法单位元)
var i = 1;
while (i < 10) {
res *= i; // 累乘
i += 2;
}
- 知识点: 累乘器必须初始化为 1(乘法的中性元素)
- 常见错误: 如果初始化为 0,结果永远是 0
- 特性: 结合步长控制(i+=2)实现奇数筛选
- 应用: 阶乘计算、组合数学、概率统计
🎯 while 循环核心特性总结
| 特性维度 | 说明 | 典型应用 |
|---|---|---|
| 前测循环 | 先判断条件,再执行循环体,可能一次都不执行 | 文件读取、数据验证 |
| 条件驱动 | 适合"不确定次数"的循环 | 用户输入直到合法、随机数生成 |
| 变量作用域 | 循环变量在循环外仍可访问(var 声明) | 需要获取循环结束时的状态 |
| 步长灵活 | 通过 i++、i--、i+=n 控制遍历间隔 |
跳跃遍历、倒序遍历 |
| 累加/累乘 | 配合辅助变量实现数据聚合 | 求和、求积、计数 |
🔑 关键规律归纳
规律1:循环变量的终值
循环结束后,i 的值 = 第一个使条件为 false 的值
示例:while(i<=15) → 结束时 i=16
while(i>=43) → 结束时 i=42
规律2:步长与遍历集合
步长 = 1 → 遍历所有整数
步长 = 2 → 遍历偶数(起点0)或奇数(起点1)
步长 = n → 遍历 n 的倍数
步长 = -1 → 倒序遍历
规律3:累加器/累乘器初始值口诀
累加用 0(0+任何数=任何数)
累乘用 1(1×任何数=任何数)
规律4:三要素检查清单
✓ 初始化:循环变量是否赋值?
✓ 条件判断:是否会终止(不是永远 true)?
✓ 变量更新:是否向终止条件靠近?
3.5 while 循环三要素记忆口诀
① 初始化:给循环变量赋起始值
② 判断:满足条件才进循环体
③ 更新:每次循环后改变循环变量
缺少任何一个 → 死循环或语法错误
4 do-while 循环
4.1 语法结构
js
do {
循环体语句;
} while (条件表达式);
注意:
while(条件)后面有分号;,这是 do-while 独有的!
4.2 执行流程(Mermaid 流程图)
true
false
开始
初始化循环变量
执行循环体
更新循环变量
条件表达式?
结束
观察: 和 while 流程图对比,do-while 的判断框在循环体之后 ,因此循环体至少执行一次。
4.3 课堂案例:do-while 循环(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>do-while 循环示例</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.output { background: #1e1e1e; color: #d4d4d4; padding: 20px; border-radius: 8px; margin: 10px 0; }
.output span { display: block; padding: 2px 0; }
h3 { color: #333; border-left: 4px solid #E91E63; padding-left: 10px; }
</style>
</head>
<body>
<h3>① 输出 7~15</h3>
<div class="output" id="out1"></div>
<h3>② 计算 100 以内数字的和</h3>
<div class="output" id="out2"></div>
<h3>③ 计算 10 以内奇数的乘积</h3>
<div class="output" id="out3"></div>
<script>
function print(id, text) {
var div = document.getElementById(id);
var span = document.createElement('span');
span.textContent = text;
div.appendChild(span);
}
// ① 循环输出数字 7~15
var i = 7;
do {
print('out1', i);
i++;
} while (i <= 15);
print('out1', '循环结束后 i = ' + i); // 16
// ② 计算 100 以内所有数字的和
var sum = 0;
var i = 1;
do {
sum += i;
i++;
} while (i <= 100);
print('out2', '1+2+...+100 = ' + sum); // 5050
// ③ 计算 10 以内所有奇数的乘积
var res = 1;
var i = 1;
do {
res *= i;
i += 2;
} while (i < 10);
print('out3', '1×3×5×7×9 = ' + res); // 945
</script>
</body>
</html>

📝 do-while 代码解释
核心特征:先执行,后判断(后测循环)
js
// do-while 执行顺序
var i = 7;
do {
// ① 先执行循环体(无条件,必执行)
print('out1', i);
i++;
// ② 执行完后才判断条件
} while (i <= 15);
案例① 正序输出(7~15)
js
var i = 7; // 初始化
do {
print('out1', i); // 先输出,再判断
i++; // 更新循环变量
} while (i <= 15); // 条件判断在后
// 结果:i = 16
- 执行流程: 输出7 → i=8 → 判断8<=15(true)→ 输出8 → ... → 输出15 → i=16 → 判断16<=15(false)→ 结束
- 总执行次数: 9 次
- 特性: 即使初始条件不满足,也会先执行一次
案例② 累加求和(1+2+...+100)
js
var sum = 0; // 累加器初始化为 0
var i = 1; // 从 1 开始
do {
sum += i; // 先累加
i++; // 再更新
} while (i <= 100); // 后判断
// 结果:sum = 5050(高斯求和公式:n(n+1)/2)
- 数学验证: 100×101÷2 = 5050 ✓
- 循环次数: 100 次
- 累加器特点: 必须初始化为 0(加法单位元)
案例③ 累乘求积(1×3×5×7×9)
js
var res = 1; // 累乘器初始化为 1
var i = 1; // 从 1(第一个奇数)开始
do {
res *= i; // 先累乘
i += 2; // 步长为 2,遍历奇数
} while (i < 10); // 后判断(注意是 <10)
// 结果:res = 945 (1×3×5×7×9)
- 步长技巧: i+=2 使循环序列变为 1,3,5,7,9(奇数)
- 累乘器特点: 必须初始化为 1(乘法单位元)
- 终止条件: i<10(不是 i<=10),因为下一个奇数是 11
⚠️ 常见错误:忘记分号
js
// ❌ 错误:do-while 的 while 后必须有分号
do {
console.log(i);
i++;
} while (i < 10) // 缺少分号!语法错误
// ✅ 正确写法
do {
console.log(i);
i++;
} while (i < 10); // 分号不能省略
do-while 与 while 对比:
| 维度 | while | do-while |
|---|---|---|
| 判断时机 | 先判断再执行 | 先执行再判断 |
| 最少执行次数 | 0 次 | 1 次 |
| 语法标识 | while(...) { } |
do { } while(...); |
| 分号要求 | 无 | while 后必须有分号 |
| 适用场景 | 可能不执行 | 至少执行一次 |
实际应用场景:
- ✅ 用户输入验证:至少让用户输入一次
- ✅ 菜单显示:至少显示一次菜单
- ✅ 游戏主循环:至少玩一局
- ✅ 随机数生成:至少生成一次再判断
5 while 与 do-while 的本质区别
5.1 概念对比
| 对比维度 | while | do-while |
|---|---|---|
| 判断时机 | 先判断后执行 | 先执行后判断 |
| 最少执行次数 | 0 次(条件一开始就不成立) | 1 次(至少执行一次循环体) |
| 语法特征 | while(...) { } |
do { } while(...); |
| 适用场景 | 不确定是否需要执行 | 至少需要执行一次的场景 |
5.2 关键区别演示(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>while vs do-while 区别</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.container { display: flex; gap: 20px; flex-wrap: wrap; }
.box { flex: 1; min-width: 300px; background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.box h3 { margin-top: 0; }
.while-box h3 { color: #2196F3; }
.dowhile-box h3 { color: #E91E63; }
.output { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 6px; font-family: monospace; }
.output span { display: block; }
.result { font-size: 1.1em; font-weight: bold; padding: 10px; border-radius: 6px; margin-top: 10px; }
.result.zero { background: #FFF3E0; color: #E65100; }
.result.one { background: #E8F5E9; color: #2E7D32; }
</style>
</head>
<body>
<h2>当初始条件不满足时(n = 10,条件 n > 10)</h2>
<div class="container">
<div class="box while-box">
<h3>while 循环</h3>
<pre style="background:#f0f0f0;padding:10px;border-radius:4px">var n = 10;
while (n > 10) {
console.log(n);
n++;
}
// 条件 10 > 10 为 false
// 循环体一次都不执行</pre>
<div class="output" id="whileOut"></div>
<div class="result zero" id="whileResult"></div>
</div>
<div class="box dowhile-box">
<h3>do-while 循环</h3>
<pre style="background:#f0f0f0;padding:10px;border-radius:4px">var n = 10;
do {
console.log(n);
n++;
} while (n > 10);
// 先执行一次,再判断
// 循环体至少执行一次</pre>
<div class="output" id="dowhileOut"></div>
<div class="result one" id="dowhileResult"></div>
</div>
</div>
<script>
function print(id, text) {
var div = document.getElementById(id);
var span = document.createElement('span');
span.textContent = text;
div.appendChild(span);
}
// while 循环(条件不成立,一次都不执行)
var count1 = 0;
var n = 10;
while (n > 10) {
print('whileOut', '执行了!n = ' + n);
n++;
count1++;
}
if (count1 === 0) {
print('whileOut', '(循环体从未执行)');
}
document.getElementById('whileResult').textContent =
'循环体共执行了 ' + count1 + ' 次';
// do-while 循环(先执行再判断,至少执行一次)
var count2 = 0;
var n = 10;
do {
print('dowhileOut', '执行了!n = ' + n);
n++;
count2++;
} while (n > 10);
document.getElementById('dowhileResult').textContent =
'循环体共执行了 ' + count2 + ' 次';
</script>
</body>
</html>

卡死
📝 代码解释:while vs do-while 关键区别实验
实验设计:初始条件不满足(n=10,条件 n>10)
while 循环分析:
js
var n = 10; // 初始值
while (n > 10) { // ① 先判断:10 > 10? → false
print('whileOut', '执行了!n = ' + n);
n++;
}
// ② 因为条件为 false,循环体完全不执行
// 结果:循环体执行 0 次
- 执行流程: 判断条件 → 发现false → 跳过循环体 → 直接结束
- 输出: 无输出,显示"(循环体从未执行)"
- count1: 0
do-while 循环分析:
js
var n = 10; // 初始值
do { // ① 先执行循环体(不判断条件)
print('dowhileOut', '执行了!n = ' + n); // 输出 10
n++; // n 变为 11
} while (n > 10); // ② 后判断:11 > 10? → true,但已执行完一次了
// 结果:循环体执行 1 次(至少一次)
- 执行流程: 执行循环体 → 输出10 → n变为11 → 判断11>10(true,但已经执行过了)
- 输出: "执行了!n = 10"
- count2: 1
关键区别可视化:
while 循环:
[判断] → [执行] → [判断] → [执行] → ...
↑ 初次判断为false → 不执行
do-while 循环:
[执行] → [判断] → [执行] → [判断] → ...
↑ 无条件先执行一次
结论:
- while: 判断条件是"门卫",条件不满足就不让进
- do-while: 先进门再判断,至少进去一次
实际应用对比:
| 场景 | 推荐循环 | 理由 |
|---|---|---|
| 读取文件 | while | 文件可能为空,不需要读取 |
| 用户输入 | do-while | 至少要让用户输入一次 |
| 数据验证 | while | 数据可能已经合法,无需处理 |
| 菜单显示 | do-while | 菜单必须至少显示一次 |
| 条件循环 | while | 不确定是否需要执行 |
| 至少一次 | do-while | 明确需要执行一次 |
记忆口诀:
while:先看门票,再进场(先判断)
do-while:先进场,再查票(先执行)
5.3 双循环流程对比(Mermaid)
do-while 循环(后测)
false
true
开始
循环体
条件?
结束,≥1次
while 循环(前测)
false
true
开始
条件?
结束,0次
循环体
5.4 实际应用场景对比
while 的典型场景: 读取文件数据------先检查文件是否有内容,再读取
js
// 模拟:先检查是否有数据,再处理
var hasData = checkDataSource(); // 可能一开始就没有数据
while (hasData) {
processNextRecord();
hasData = checkDataSource();
}
do-while 的典型场景: 用户输入验证------至少要让用户输入一次
js
// 至少弹出一次输入框,直到输入正确
do {
var input = prompt('请输入一个正数:');
var num = parseFloat(input);
} while (isNaN(num) || num <= 0);
console.log('输入的正数是:', num);
6 for 循环
6.1 语法结构
js
for (初始化表达式; 条件表达式; 更新表达式) {
循环体语句;
}
6.2 四个部分的详细解析
js
for (var i = 1; i <= 100; i++) {
// ①初始化 ②条件判断 ③更新
sum += i; // ④循环体
}
| 部分 | 名称 | 执行时机 | 执行次数 |
|---|---|---|---|
var i = 1 |
初始化表达式 | 循环开始前执行一次 | 1 次 |
i <= 100 |
条件表达式 | 每次循环前先判断 | n+1 次(多一次失败判断) |
i++ |
更新表达式 | 循环体执行之后 | n 次 |
sum += i |
循环体 | 条件为 true 时 | n 次 |
6.3 执行流程(Mermaid 流程图)
true
false
开始
① 初始化 var i = 1
② 条件 i <= 100 ?
④ 执行循环体 sum += i
③ 更新 i++
结束
6.4 课堂案例:for 循环(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>for 循环示例</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.demo-section { background: white; border-radius: 8px; padding: 20px; margin: 15px 0; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.demo-section h3 { margin-top: 0; color: #9C27B0; border-bottom: 2px solid #E1BEE7; padding-bottom: 8px; }
.output { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 6px; font-family: monospace; max-height: 200px; overflow-y: auto; }
.output span { display: block; }
.answer { background: #E8F5E9; color: #2E7D32; padding: 10px; border-radius: 6px; margin-top: 10px; font-weight: bold; font-size: 1.1em; }
</style>
</head>
<body>
<div class="demo-section">
<h3>① 循环输出数字 7~15</h3>
<div class="output" id="out1"></div>
</div>
<div class="demo-section">
<h3>② 倒序输出 56~43</h3>
<div class="output" id="out2"></div>
</div>
<div class="demo-section">
<h3>③ 输出 10 以内所有偶数(步长为 2)</h3>
<div class="output" id="out3"></div>
</div>
<div class="demo-section">
<h3>④ 计算 100 以内所有数字的和</h3>
<div class="output" id="out4"></div>
<div class="answer" id="ans4"></div>
</div>
<div class="demo-section">
<h3>⑤ 计算 10 以内所有奇数的乘积</h3>
<div class="output" id="out5"></div>
<div class="answer" id="ans5"></div>
</div>
<script>
function print(id, text, isAnswer) {
var container = document.getElementById(id);
if (isAnswer) {
container.textContent = text;
return;
}
var span = document.createElement('span');
span.textContent = text;
container.appendChild(span);
}
// ① 循环输出数字 7~15
for (var num = 7; num <= 15; num++) {
print('out1', num);
}
print('out1', '循环结束后 num = ' + num); // 16
// ② 倒序输出 56~43(步长为 -1)
for (var i = 56; i >= 43; i--) {
print('out2', i);
}
print('out2', '循环结束后 i = ' + i); // 42
// ③ 输出 10 以内所有偶数
for (var i = 0; i <= 10; i += 2) {
print('out3', i);
}
print('out3', '循环结束后 i = ' + i); // 12
// ④ 计算 100 以内所有数字的和
var sum = 0;
for (var i = 1; i <= 100; i++) {
sum += i;
}
print('out4', '计算过程:1 + 2 + 3 + ... + 100');
print('ans4', '结果:sum = ' + sum + ' (高斯公式:100×101÷2 = 5050)', true);
// ⑤ 计算 10 以内所有奇数的乘积
var res = 1;
for (var i = 1; i <= 10; i += 2) {
res *= i;
}
print('out5', '计算过程:1 × 3 × 5 × 7 × 9');
print('ans5', '结果:res = ' + res + ' (1×3×5×7×9 = 945)', true);
</script>
</body>
</html>

6.5 for 循环执行步骤逐帧分析
以 for (var i = 1; i <= 3; i++) { console.log(i); } 为例:
更新 循环体 条件判断 初始化 引擎 更新 循环体 条件判断 初始化 引擎 var i = 1 判断 1 <= 3? → true 执行 console.log(1) i++ → i = 2 判断 2 <= 3? → true 执行 console.log(2) i++ → i = 3 判断 3 <= 3? → true 执行 console.log(3) i++ → i = 4 判断 4 <= 3? → false 循环结束,i = 4
📝 for 循环执行顺序深度解析
完整执行序列:
第 1 步:执行初始化(仅 1 次) → var i = 1
第 2 步:判断条件(第 1 次) → 1 <= 3? true
第 3 步:执行循环体(第 1 次) → console.log(1)
第 4 步:执行更新(第 1 次) → i++ (i=2)
第 5 步:判断条件(第 2 次) → 2 <= 3? true
第 6 步:执行循环体(第 2 次) → console.log(2)
第 7 步:执行更新(第 2 次) → i++ (i=3)
第 8 步:判断条件(第 3 次) → 3 <= 3? true
第 9 步:执行循环体(第 3 次) → console.log(3)
第 10 步:执行更新(第 3 次) → i++ (i=4)
第 11 步:判断条件(第 4 次) → 4 <= 3? false
第 12 步:跳出循环 → i 保持为 4
核心规律总结:
| 执行部分 | 执行次数 | 执行时机 | 特点 |
|---|---|---|---|
| 初始化 | 1 次 | 循环开始前 | 只执行一次,设置起点 |
| 条件判断 | n+1 次 | 每次循环前 + 最后一次失败判断 | 比循环体多执行 1 次 |
| 循环体 | n 次 | 条件为 true 时 | 实际业务逻辑 |
| 更新表达式 | n 次 | 循环体之后 | 推进循环变量 |
关键特性提炼:
-
初始化只执行 1 次
jsfor (var i = 0; i < 3; i++) { ... } // var i = 0 只在最开始执行一次 // 不会每次循环都重新初始化 -
条件判断比循环体多 1 次
js// 循环体执行 3 次(i=0,1,2) // 条件判断执行 4 次(i=0,1,2,3) // 最后一次判断 3<3 为 false,跳出循环 -
更新表达式在循环体之后
jsfor (var i = 0; i < 3; i++) { console.log(i); // 先输出当前 i // 然后执行 i++ } // 输出:0 1 2(不是 1 2 3) -
循环变量的终值
jsfor (var i = 0; i < 5; i++) { } console.log(i); // 5(第一个不满足条件的值)
🎯 for 循环与 while 循环等价转换
js
// for 循环标准形式
for (var i = 0; i < 10; i++) {
console.log(i);
}
// 等价的 while 循环
var i = 0; // ← 初始化移到循环外
while (i < 10) { // ← 条件判断
console.log(i); // ← 循环体
i++; // ← 更新移到循环体末尾
}
转换规律:
for (初始化; 条件; 更新) {
循环体;
}
等价于 ↓
初始化;
while (条件) {
循环体;
更新;
}
选择建议:
- 已知循环次数 → 用 for(三要素集中,更简洁)
- 不确定次数 → 用 while(条件驱动,更灵活)
7 for 循环的特殊结构
for 循环的三个表达式都可以省略或移出,但分号不能省略。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>特殊结构的 for 循环</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.code-block { background: #1e1e1e; color: #d4d4d4; padding: 20px; border-radius: 8px; margin: 15px 0; font-family: monospace; line-height: 1.8; }
.keyword { color: #569CD6; }
.comment { color: #6A9955; }
.number { color: #B5CEA8; }
h3 { color: #FF9800; border-left: 4px solid #FF9800; padding-left: 10px; }
.output { background: #263238; color: #ECEFF1; padding: 10px 20px; border-radius: 6px; }
.output span { display: block; }
</style>
</head>
<body>
<h2>for 循环特殊结构演示</h2>
<h3>① 标准结构</h3>
<div class="code-block">
<span><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i < <span class="number">10</span>; i += <span class="number">2</span>) { ... }</span>
</div>
<h3>② 初始化写在外面</h3>
<div class="code-block">
<span><span class="keyword">var</span> i = <span class="number">1</span>;</span>
<span><span class="keyword">for</span> (; i < <span class="number">10</span>; i += <span class="number">2</span>) { ... } <span class="comment">// 第一个分号不能省!</span></span>
</div>
<h3>③ 初始化和更新都写在外面(退化为 while)</h3>
<div class="code-block">
<span><span class="keyword">var</span> i = <span class="number">1</span>;</span>
<span><span class="keyword">for</span> (; i < <span class="number">10</span>; ) { <span class="comment">// 等价于 while</span></span>
<span> console.log(i);</span>
<span> i += <span class="number">2</span>;</span>
<span>}</span>
</div>
<h3>④ 使用两个循环标记变量</h3>
<div class="code-block">
<span><span class="keyword">for</span> (x=<span class="number">1</span>,y=<span class="number">1</span>; x<=<span class="number">15</span>,y<=<span class="number">10</span>; x++,y++) { ... }</span>
<span class="comment">// 注意:逗号表达式的条件判断,取最后一个表达式的值</span>
</div>
<h3>⑤ 死循环写法(了解即可)</h3>
<div class="code-block">
<span><span class="keyword">for</span> (;;) { <span class="comment">// 三个表达式全部省略 → 死循环</span></span>
<span> <span class="comment">// 需要配合 break 退出</span></span>
<span>}</span>
</div>
<h3>两变量循环输出结果:</h3>
<div class="output" id="out"></div>
<script>
function print(id, text) {
var div = document.getElementById(id);
var span = document.createElement('span');
span.textContent = text;
div.appendChild(span);
}
// 两个循环标记变量
for (var x = 1, y = 1; x <= 15, y <= 10; x++, y++) {
print('out', 'x=' + x + ', y=' + y);
}
print('out', '循环结束:x=' + x + ', y=' + y);
</script>
</body>
</html>

📝 代码解释:for 循环的特殊结构
① 标准结构(推荐写法)
js
for (var i = 1; i < 10; i += 2) {
// ↑ 初始化 ↑ 条件 ↑ 更新 三个表达式都在括号里
console.log(i); // 输出:1, 3, 5, 7, 9
}
② 省略初始化(初始化移到外部)
js
var i = 1; // 初始化移到外面
for (; i < 10; i += 2) { // 第一个分号必须保留!
console.log(i);
}
// 用途:当循环变量需要在循环外使用时,可以先声明在外
- ⚠️ 注意:分号不能省略,即使表达式为空,分号占位符依然必须存在
③ 省略全部(退化为 while)
js
var i = 1;
for (; i < 10;) { // 等价于 while(i < 10)
console.log(i);
i += 2; // 更新语句移进循环体
}
// 这就是 for 与 while 完全等价的形式
④ 两个循环变量同时控制
js
for (var x = 1, y = 1; x <= 15, y <= 10; x++, y++) {
// ↑ 逗号运算符初始化两个变量
// ↑ 逗号表达式,取最后一个值(y<=10)
// ↑ 两个变量同时更新
console.log('x=' + x + ', y=' + y);
}
// 循环结束时:x = 11, y = 11(因为 y <= 10 先不满足)
// ⚠️ 关键:逗号条件表达式取最后一个的值
// x<=15, y<=10 → 取 y<=10 的值
// 所以循环由 y<=10 决定,执行 10 次
⑤ 死循环写法(了解即可)
js
for (;;) { // 三个表达式全部省略 → 无限循环
// 循环体
if (满足退出条件) break; // 必须用 break 退出
}
// 等价于 while(true) { ... }
// 用途:不确定循环次数,靠内部 break 控制退出
逗号运算符详解:
js
// 逗号运算符:从左到右依次求值,返回最后一个表达式的值
var result = (1, 2, 3); // result = 3(返回最后一个)
// 在 for 循环的条件位置:
for (; x<=15, y<=10; ) {
// ↑ 先求 x<=15,再求 y<=10,用 y<=10 的值作为循环条件
}
各种写法等价转换:
js
// 标准 for
for (var i = 0; i < 5; i++) {
console.log(i);
}
// 等价 while(省略 for 的 init 和 update)
var i = 0;
for (; i < 5;) {
console.log(i);
i++;
}
// 等价 while(直接写 while)
var i = 0;
while (i < 5) {
console.log(i);
i++;
}
// 三种写法完全等价,结果相同
7.1 特殊结构总结表
| 变种形式 | 写法 | 说明 |
|---|---|---|
| 标准 | for(init; cond; upd){} |
最常用,推荐 |
| 省略初始化 | for(; cond; upd){} |
初始化变量在外部 |
| 省略更新 | for(; cond;){} |
更新语句写在循环体中,等价 while |
| 省略全部 | for(;;){} |
死循环,必须配合 break |
| 多变量 | for(i=0,j=0; ...; i++,j++){} |
同时控制多个变量 |
⚠️ 核心注意事项:
1. 分号(;)不能省略:for 循环括号内必须有两个分号
2. 逗号条件:x<=15, y<=10 取最后一个值 y<=10 决定循环
3. for(;;) 是死循环,必须配合 break 使用
4. 多变量适用于需要同步推进两个计数器的场景
8 不需要标记变量的循环
有些循环目标是"满足某个随机条件",无法预知迭代次数,这时就不需要计数器。
8.1 完整案例(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>不需要标记变量的循环</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.card { background: white; border-radius: 8px; padding: 20px; margin: 15px 0; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.card h3 { margin-top: 0; color: #00BCD4; }
button { background: #00BCD4; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 1em; }
button:hover { background: #0097A7; }
.result { background: #E0F7FA; border: 2px solid #00BCD4; border-radius: 6px; padding: 15px; margin-top: 10px; font-weight: bold; font-size: 1.1em; }
.stats { color: #777; font-size: 0.9em; }
</style>
</head>
<body>
<div class="card">
<h3>① 使用 while 循环:取大于 0.99 的随机数</h3>
<button onclick="demo1()">运行</button>
<div class="result" id="res1">点击运行</div>
<div class="stats" id="stats1"></div>
</div>
<div class="card">
<h3>② 使用 do-while:取小于 0.001 的随机数</h3>
<button onclick="demo2()">运行</button>
<div class="result" id="res2">点击运行</div>
<div class="stats" id="stats2"></div>
</div>
<div class="card">
<h3>③ 使用 for 循环:取 0.59~0.61 之间的随机数</h3>
<button onclick="demo3()">运行</button>
<div class="result" id="res3">点击运行</div>
<div class="stats" id="stats3"></div>
</div>
<script>
function demo1() {
var count = 0;
var rand = Math.random();
while (rand <= 0.99) {
rand = Math.random();
count++;
}
document.getElementById('res1').textContent = '找到的随机数:' + rand.toFixed(6);
document.getElementById('stats1').textContent = '尝试次数:' + count + ' 次(理论上约需 100 次)';
}
function demo2() {
var count = 0;
do {
var rand = Math.random();
count++;
} while (rand >= 0.001);
document.getElementById('res2').textContent = '找到的随机数:' + rand.toFixed(6);
document.getElementById('stats2').textContent = '尝试次数:' + count + ' 次(理论上约需 1000 次)';
}
function demo3() {
var count = 0;
for (var rand = Math.random(); rand <= 0.59 || rand >= 0.61; rand = Math.random()) {
count++;
}
document.getElementById('res3').textContent = '找到的随机数:' + rand.toFixed(6);
document.getElementById('stats3').textContent = '尝试次数:' + count + ' 次(理论上约需 50 次)';
}
</script>
</body>
</html>

📝 代码说明与特性提炼
案例① while 循环模式(条件后置)
js
var rand = Math.random(); // 【初始化】先生成一个随机数
while (rand <= 0.99) { // 【条件判断】如果不符合要求
rand = Math.random(); // 【循环体】重新生成
count++; // 【计数】记录尝试次数
}
- 特性: 需要先生成一个初始值,然后判断是否继续
- 适用场景: 初始值可能直接满足条件(可能 0 次循环)
- 理论分析: 概率 P=0.01,期望尝试次数 = 1/P = 100 次
案例② do-while 循环模式(条件前置)
js
do {
var rand = Math.random(); // 【循环体】至少生成一次
count++;
} while (rand >= 0.001); // 【条件判断】检查是否需要继续
- 特性: 保证至少执行一次循环体
- 优势: 不需要在循环外单独初始化
- 适用场景: 必须至少尝试一次的情况
- 理论分析: 概率 P=0.001,期望尝试次数 = 1000 次
案例③ for 循环模式(精简语法)
js
// 【初始化】 【条件判断】 【更新】
for (var rand = Math.random(); rand <= 0.59 || rand >= 0.61; rand = Math.random()) {
count++; // 【循环体】仅计数
}
- 特性: 将随机数生成写在初始化和更新表达式中
- 优势: 语法紧凑,循环变量(rand)作用域清晰
- 技巧: 复合条件(|| 或运算)筛选区间外的值
- 理论分析: 概率 P=0.02(区间宽度),期望尝试次数 = 50 次
🎯 三种循环在"无标记变量"场景下的对比
| 循环类型 | 语法特点 | 初始化位置 | 适用场景 |
|---|---|---|---|
| while | 传统,易读 | 循环外 | 初始值可能直接满足 |
| do-while | 保证执行 | 循环内 | 必须至少执行一次 |
| for | 紧凑,现代 | 初始化表达式 | 变量仅循环内使用 |
🔑 概率与期望循环次数
js
// 数学原理:几何分布
// 如果每次成功的概率是 p,那么期望尝试次数 = 1/p
示例:
- 找 >0.99 的随机数:p = 1-0.99 = 0.01 → 期望 100 次
- 找 <0.001 的随机数:p = 0.001 → 期望 1000 次
- 找 0.59~0.61 区间:p = 0.02 → 期望 50 次
实际测试: 每次运行结果会波动,但多次平均会接近期望值(大数定律)
8.2 实际应用场景
这种"不需要标记变量的循环"在真实项目中非常常见:
1. 密码强度验证
js
// 循环生成随机密码直到满足强度要求
do {
var password = generateRandomPassword();
} while (!isStrongEnough(password));
2. ID 碰撞检测
js
// 循环生成 ID 直到不重复
var existingIds = new Set(getAllIds());
do {
var newId = generateUUID();
} while (existingIds.has(newId));
3. 网络请求重试机制
js
// 循环重试直到成功或超过最大次数
var maxRetries = 3;
var retries = 0;
while (retries < maxRetries) {
try {
var response = await fetch(url);
if (response.ok) break; // 成功,跳出
} catch (error) {
retries++;
}
}
4. 游戏随机事件
js
// 循环投骰子直到出现特定点数
var targetNumber = 6;
do {
var dice = Math.floor(Math.random() * 6) + 1;
} while (dice !== targetNumber);
特点总结:
- ✅ 循环次数不确定,由随机性或外部条件决定
- ✅ 不需要传统的计数器变量(i、j、k)
- ✅ 通常配合
break或复杂条件终止 - ✅ 常用于:随机算法、重试逻辑、搜索匹配
9 循环嵌套
9.1 概念
循环体中包含另一个循环,称为嵌套循环(Nested Loop)。
规律:
- 外层循环控制行数
- 内层循环控制每行的列数
- 总执行次数 = 外层次数 × 内层次数
9.2 执行顺序可视化(Mermaid)
true
true
false
false
开始
外层 i=1
i <= 3?
内层 j=1
j <= 3?
执行: i,j
j++
i++
结束
9.3 动态表格案例(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>嵌套循环 - 动态表格</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: #f5f5f5; }
.controls { background: white; border-radius: 8px; padding: 20px; margin-bottom: 15px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); display: flex; gap: 15px; align-items: center; flex-wrap: wrap; }
.controls label { font-weight: bold; }
.controls input { width: 60px; padding: 8px; border: 2px solid #ddd; border-radius: 6px; font-size: 1em; }
.controls button { background: #4CAF50; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 1em; }
.controls button:hover { background: #388E3C; }
table { width: 100%; table-layout: fixed; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
td { padding: 12px; border: 1px solid #e0e0e0; text-align: center; }
tr:nth-child(even) td { background: #F5F5F5; }
tr:nth-child(odd) td { background: white; }
.info { color: #666; font-size: 0.9em; margin-top: 10px; }
</style>
</head>
<body>
<h2>嵌套循环:动态生成表格</h2>
<div class="controls">
<label>行数:<input type="number" id="rows" value="5" min="1" max="20"></label>
<label>列数:<input type="number" id="cols" value="6" min="1" max="20"></label>
<button onclick="generate()">生成表格</button>
</div>
<div id="tableContainer"></div>
<div class="info" id="info"></div>
<script>
function generate() {
var rows = parseInt(document.getElementById('rows').value) || 5;
var cols = parseInt(document.getElementById('cols').value) || 6;
var html = '<table><tbody>';
var totalCells = 0;
// 外层循环:控制行
for (var i = 0; i < rows; i++) {
html += '<tr>';
// 内层循环:控制列
for (var j = 0; j < cols; j++) {
html += '<td>第' + (i+1) + '行<br>第' + (j+1) + '列</td>';
totalCells++;
}
html += '</tr>';
}
html += '</tbody></table>';
document.getElementById('tableContainer').innerHTML = html;
document.getElementById('info').textContent =
'共 ' + rows + ' 行 × ' + cols + ' 列 = ' + totalCells + ' 个单元格';
}
generate(); // 初始执行
</script>
</body>
</html>

📝 代码解释:嵌套循环动态生成表格
核心知识:嵌套循环 = 行 × 列
① 行列关系:
js
// 外层循环 i:控制"行"
for (var i = 0; i < rows; i++) {
// 内层循环 j:控制"列"
for (var j = 0; j < cols; j++) {
// 每个 (i, j) 组合对应一个单元格
}
}
// 总单元格数 = rows × cols
② 字符串拼接生成 HTML:
js
var html = '<table><tbody>';
for (var i = 0; i < rows; i++) {
html += '<tr>'; // 开始一行
for (var j = 0; j < cols; j++) {
html += '<td>第' + (i+1) + '行<br>第' + (j+1) + '列</td>';
// ↑ i 从 0 开始,+1 转成人类易读的 1
totalCells++; // 统计总单元格数
}
html += '</tr>'; // 结束一行
}
html += '</tbody></table>';
container.innerHTML = html; // 一次性写入 DOM(性能更好)
③ 执行顺序演示(3行2列为例):
i=0(第1行):
j=0 → 第1行第1列
j=1 → 第1行第2列
i=1(第2行):
j=0 → 第2行第1列
j=1 → 第2行第2列
i=2(第3行):
j=0 → 第3行第1列
j=1 → 第3行第2列
总计:3 × 2 = 6 个单元格
④ 嵌套循环执行次数分析:
js
// 外层执行 rows 次
// 每次外层执行,内层执行 cols 次
// 总执行次数 = rows × cols
// 例:5行6列
// 外层:5 次
// 内层:每次 6 次,共 5 × 6 = 30 次
// totalCells = 30
⑤ 性能优化:字符串拼接 vs 逐个 appendChild
js
// ✅ 推荐:先拼接字符串,最后一次写入 DOM(减少 DOM 操作次数)
var html = '';
for (...) {
html += '<td>...</td>';
}
container.innerHTML = html; // 只触发一次 DOM 重排
// ❌ 低效:每次循环都触发 DOM 操作
for (...) {
var td = document.createElement('td');
container.appendChild(td); // 每次都触发 DOM 重排
}
⑥ 输入解析:
js
var rows = parseInt(document.getElementById('rows').value) || 5;
// ↑ 字符串转整数 ↑ 默认值(空或0时用5)
嵌套循环规律总结:
| 特性 | 说明 | 示例 |
|---|---|---|
| 外层控制行 | 外层迭代次数 = 行数 | for (i = 0; i < rows; i++) |
| 内层控制列 | 内层迭代次数 = 列数 | for (j = 0; j < cols; j++) |
| 总迭代次数 | 外层次数 × 内层次数 | rows × cols |
| 坐标表示 | (i, j) 唯一定位单元格 | 第 i+1 行,第 j+1 列 |
| 时间复杂度 | O(n²) | n 为行/列数 |
实际应用场景:
- 📊 Excel/表格生成
- 📅 日历渲染(7列 × N行)
- 🎮 游戏棋盘(围棋、五子棋)
- 🗺️ 地图网格(像素编辑器)
10 九九乘法表四种形式
九九乘法表是学习嵌套循环的经典案例,掌握它的四种变形是理解循环逻辑的关键。
10.1 四种形式的规律总结
四种形式
① 左下三角
外层1→9 内层1→i
② 左上三角
外层9→1 内层1→i
③ 右下三角
外层1→9 内层1→i
前补空格
④ 右上三角
外层9→1 内层1→i
前补空格
| 形式 | 外层方向 | 内层范围 | 占位空格 | 效果 |
|---|---|---|---|---|
| 第一种 | 1 → 9 | 1 → i | 无 | 左下三角 |
| 第二种 | 9 → 1 | 1 → i | 无 | 左上三角 |
| 第三种 | 1 → 9 | 1 → i | 前补 9-i 个空格 | 右下三角 |
| 第四种 | 9 → 1 | 1 → i | 前补 9-i 个空格 | 右上三角 |
10.2 第一种:左下三角(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>九九乘法表 - 第一种(左下三角)</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; }
h2 { color: white; text-align: center; }
table { border-collapse: collapse; margin: 0 auto; }
td { padding: 8px 12px; border: 1px solid rgba(255,255,255,0.3); background: rgba(255,255,255,0.1); color: white; text-align: center; font-size: 0.9em; white-space: nowrap; border-radius: 4px; }
td:hover { background: rgba(255,255,255,0.3); transition: 0.2s; }
</style>
</head>
<body>
<h2>九九乘法表 · 第一种(左下三角)</h2>
<script>
document.write('<table><tbody>');
// 【外层循环】控制行数(第几行)
for (var i = 1; i <= 9; i++) { // i: 1→9,共9行
document.write('<tr>'); // 开始一行
// 【内层循环】控制列数(每行几列)
// 关键:j 的范围是 1 到 i,形成左下三角
for (var j = 1; j <= i; j++) { // j: 1→i,第i行有i列
// 输出公式:j × i = 结果
document.write('<td>' + j + '×' + i + '=' + (j * i) + '</td>');
}
document.write('</tr>'); // 结束一行
}
document.write('</tbody></table>');
</script>
</body>
</html>

📝 代码逐行解析
核心逻辑拆解:
js
// 第1行:i=1,j从1到1 → 输出 1×1=1
// 第2行:i=2,j从1到2 → 输出 1×2=2, 2×2=4
// 第3行:i=3,j从1到3 → 输出 1×3=3, 2×3=6, 3×3=9
// ...
// 第9行:i=9,j从1到9 → 输出 1×9=9 ... 9×9=81
执行次数分析:
外层循环:9 次(i: 1→9)
内层循环:
i=1 时:1 次(j: 1→1)
i=2 时:2 次(j: 1→2)
i=3 时:3 次(j: 1→3)
...
i=9 时:9 次(j: 1→9)
总单元格数 = 1+2+3+...+9 = 9×(9+1)÷2 = 45 个
(等差数列求和公式)
关键特性:
- 内层循环范围动态变化 :
j <= i使得每行列数等于行号 - 左对齐效果:第1行1列,第2行2列,形成左下三角
- 被乘数(j)在前 :每行是
1×i, 2×i, 3×i... - DOM 操作 :使用
document.write()动态生成 HTML
🎯 知识点总结
嵌套循环的核心规律:
外层循环:控制"大方向"(行数/轮次)
内层循环:控制"细节"(每行的列数/每轮的次数)
内层变量范围:常依赖外层变量(j <= i)
左下三角形态的数学本质:
第 n 行有 n 列
总单元格数 = Σ(i=1 to n) i = n(n+1)/2
这是等差数列求和,时间复杂度 O(n²)
10.3 第二种:左上三角(完整可运行 HTML)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>九九乘法表 - 第二种(左上三角)</title>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; padding: 20px; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); min-height: 100vh; }
h2 { color: white; text-align: center; }
table { border-collapse: collapse; margin: 0 auto; }
td { padding: 8px 12px; border: 1px solid rgba(255,255,255,0.3); background: rgba(255,255,255,0.1); color: white; text-align: center; font-size: 0.9em; white-space: nowrap; border-radius: 4px; }
td:hover { background: rgba(255,255,255,0.3); transition: 0.2s; }
</style>
</head>
<body>
<h2>九九乘法表 · 第二种(左上三角)</h2>
<script>
document.write('<table><tbody>');
// 外层:i 从 9 到 1(倒序,最大行在顶部)
for (var i = 9; i >= 1; i--) {
document.write('<tr>');
// 内层:j 从 1 到 i
for (var j = 1; j <= i; j++) {
document.write('<td>' + j + '×' + i + '=' + (j * i) + '</td>');
}
document.write('</tr>');
}
document.write('</tbody></table>');
</script>
</body>
</html>

📝 代码解释:第二种(左上三角)- 倒序外层
与第一种的唯一区别:外层循环倒序
js
// 第一种:左下三角(正序)
for (var i = 1; i <= 9; i++) { // i: 1→9(第1行1个,第9行9个)
for (var j = 1; j <= i; j++) { // 内层从1到i
// 第i行输出 i 个乘法式
}
}
// 第二种:左上三角(倒序)
for (var i = 9; i >= 1; i--) { // i: 9→1(第1行9个,第9行1个)
for (var j = 1; j <= i; j++) { // 内层仍从1到i
// 第1行:j: 1→9(9个),第2行:j: 1→8(8个)...
}
}
视觉效果对比:
第一种(左下三角): 第二种(左上三角):
1×1=1 1×9=9 2×9=18 ... 9×9=81
1×2=2 2×2=4 1×8=8 2×8=16 ... 8×8=64
1×3=3 2×3=6 3×3=9 ...
... 1×2=2 2×2=4
1×1=1
执行顺序详解(外层倒序):
i=9:j: 1→9(9个) → 最长的一行在顶部
i=8:j: 1→8(8个)
i=7:j: 1→7(7个)
...
i=1:j: 1→1(1个) → 最短的一行在底部
document.write 方式:
js
document.write('<td>' + j + '×' + i + '=' + (j * i) + '</td>');
// ↑ HTML 实体:× 符号的 HTML 写法
// 输出:<td>1×9=9</td>
关键点总结:
| 特性 | 第一种(左下) | 第二种(左上) |
|---|---|---|
| 外层方向 | 正序 i: 1→9 | 倒序 i: 9→1 |
| 每行数量 | 第i行有i个 | 第(10-i)行有i个 |
| 顶部 | 最少(1个) | 最多(9个) |
| 底部 | 最多(9个) | 最少(1个) |
| 内层逻辑 | j: 1→i(相同) |
j: 1→i(相同) |
记忆口诀:
左上三角 = 左下三角翻转
只需把外层 i: 1→9 改为 i: 9→1
其他代码完全相同!