DAY_20JavaScript 条件语句与循环结构深度学习(一)

------分支判断 · 三大循环 · 跳转控制 · 实战案例 · 性能优化全解析

知识定位: JavaScript 流程控制 → 条件分支 → 循环结构 → 跳转语句
适合人群: 前端初学者、正在系统学习 JS 的同学
涵盖内容: if/else · switch · while · do-while · for · 嵌套循环 · break · continue · 九九乘法表四种形式 · 性能优化 · 面试高频考点
内容规模: 24 章 · 80+ 知识点 · 19 个完整可运行示例 · 9 道经典练习题


目录

  1. 回顾:分支语句总结
    • [关键回顾:switch 的穿透(fall-through)现象](#关键回顾:switch 的穿透(fall-through)现象)
  2. 名词解释与术语表
  3. [while 循环](#while 循环)
    • [3.1 语法结构](#3.1 语法结构)
    • [3.2 执行流程](#3.2 执行流程)
    • [3.3 实现「可结束循环」的两个必要条件](#3.3 实现「可结束循环」的两个必要条件)
    • [3.4 课堂案例](#3.4 课堂案例)
    • [3.5 三要素记忆口诀](#3.5 三要素记忆口诀)
  4. [do-while 循环](#do-while 循环)
    • [4.1 语法结构](#4.1 语法结构)
    • [4.2 执行流程](#4.2 执行流程)
    • [4.3 课堂案例](#4.3 课堂案例)
  5. [while 与 do-while 的本质区别](#while 与 do-while 的本质区别)
    • [5.1 概念对比](#5.1 概念对比)
    • [5.2 关键区别演示](#5.2 关键区别演示)
    • [5.3 双循环流程对比(Mermaid)](#5.3 双循环流程对比(Mermaid))
    • [5.4 实际应用场景对比](#5.4 实际应用场景对比)
  6. [for 循环](#for 循环)
    • [6.1 语法结构](#6.1 语法结构)
    • [6.2 四个部分的详细解析](#6.2 四个部分的详细解析)
    • [6.3 执行流程](#6.3 执行流程)
    • [6.4 课堂案例](#6.4 课堂案例)
    • [6.5 for 循环执行步骤逐帧分析](#6.5 for 循环执行步骤逐帧分析)
  7. [for 循环的特殊结构](#for 循环的特殊结构)
    • [7.1 特殊结构总结表](#7.1 特殊结构总结表)
  8. 不需要标记变量的循环
    • [8.1 完整案例](#8.1 完整案例)
    • [8.2 实际应用场景](#8.2 实际应用场景)
  9. 循环嵌套
    • [9.1 概念](#9.1 概念)
    • [9.2 执行顺序可视化(Mermaid)](#9.2 执行顺序可视化(Mermaid))
    • [9.3 动态表格案例](#9.3 动态表格案例)
  10. 九九乘法表四种形式
    • [10.1 四种形式的规律总结](#10.1 四种形式的规律总结)
    • [10.2 第一种:左下三角](#10.2 第一种:左下三角)
    • [10.3 第二种:左上三角](#10.3 第二种:左上三角)
    • [10.4 第三种:右下三角](#10.4 第三种:右下三角)
    • [10.5 第四种:右上三角](#10.5 第四种:右上三角)
    • [10.6 知识点提炼](#10.6 知识点提炼)
  11. [跳转语句: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 总结)
  12. 省略大括号的写法
  13. 分支作业案例讲解
    • [13.1 案例一:字符串拼接](#13.1 案例一:字符串拼接)
    • [13.2 案例二:阶梯水价](#13.2 案例二:阶梯水价)
    • [13.3 案例三:体重判断](#13.3 案例三:体重判断)
    • [13.4 案例四:闰年判断](#13.4 案例四:闰年判断)
  14. 强化练习题完整解答
    • [练习一: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个)
  15. 默写题目与答案解析
  16. 三种循环横向对比总结
    • [16.1 语法对比](#16.1 语法对比)
    • [16.2 选择指南](#16.2 选择指南)
    • [16.3 三种循环等价转换](#16.3 三种循环等价转换)
  17. 经典场景:循环在真实项目中的应用
    • [17.1 电商网站:商品列表渲染](#17.1 电商网站:商品列表渲染)
    • [17.2 倒计时功能(循环 + 定时器)](#17.2 倒计时功能(循环 + 定时器))
    • [17.3 表单验证(循环检查多个输入)](#17.3 表单验证(循环检查多个输入))
    • [17.4 数据可视化:动态图表生成](#17.4 数据可视化:动态图表生成)
    • [17.5 分页功能(循环生成页码)](#17.5 分页功能(循环生成页码))
    • [17.6 搜索过滤功能(循环遍历过滤)](#17.6 搜索过滤功能(循环遍历过滤))
  18. 循环性能优化建议(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. 常见错误与避坑指南
    • [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. 循环进阶知识(扩展阅读)
    • [20.1 循环标签(Label)与多层跳出](#20.1 循环标签(Label)与多层跳出)
    • [20.2 循环与闭包(经典陷阱)](#20.2 循环与闭包(经典陷阱))
    • [20.3 循环与异步(Promise 在循环中的使用)](#20.3 循环与异步(Promise 在循环中的使用))
    • [20.4 循环在不同 JavaScript 引擎中的差异](#20.4 循环在不同 JavaScript 引擎中的差异)
  21. 知识点专业总结与对比
    • [21.1 三种循环的本质区别](#21.1 三种循环的本质区别)
    • [21.2 循环选择决策树](#21.2 循环选择决策树)
    • [21.3 性能对比总结(2026 实测数据)](#21.3 性能对比总结(2026 实测数据))
    • [21.4 循环与函数式编程对比](#21.4 循环与函数式编程对比)
  22. 真实项目案例分析
    • [22.1 大型网站中的循环应用](#22.1 大型网站中的循环应用)
  23. 面试高频考点
    • [23.1 循环相关面试题](#23.1 循环相关面试题)
    • [23.2 算法题中的循环应用](#23.2 算法题中的循环应用)
  24. 参考资源与延伸阅读
    • [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 用于追踪循环状态的变量,通常叫 ijk
死循环 Infinite Loop 条件永远为 true,永远不会结束的循环,会导致页面卡死
迭代 Iteration 每一次循环执行称为一次迭代
嵌套循环 Nested Loop 循环内部再包含循环
跳转语句 Jump Statement 改变程序正常执行流程的语句,如 breakcontinue
穿透 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. 初始化只执行 1 次

    js 复制代码
    for (var i = 0; i < 3; i++) { ... }
    // var i = 0 只在最开始执行一次
    // 不会每次循环都重新初始化
  2. 条件判断比循环体多 1 次

    js 复制代码
    // 循环体执行 3 次(i=0,1,2)
    // 条件判断执行 4 次(i=0,1,2,3)
    // 最后一次判断 3<3 为 false,跳出循环
  3. 更新表达式在循环体之后

    js 复制代码
    for (var i = 0; i < 3; i++) {
        console.log(i);  // 先输出当前 i
        // 然后执行 i++
    }
    // 输出:0 1 2(不是 1 2 3)
  4. 循环变量的终值

    js 复制代码
    for (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 &lt; <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 &lt; <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 &lt; <span class="number">10</span>; ) { <span class="comment">// 等价于 while</span></span>
        <span>&nbsp;&nbsp;&nbsp;&nbsp;console.log(i);</span>
        <span>&nbsp;&nbsp;&nbsp;&nbsp;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&lt;=<span class="number">15</span>,y&lt;=<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>&nbsp;&nbsp;&nbsp;&nbsp;<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 + '&times;' + 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 个
(等差数列求和公式)

关键特性:

  1. 内层循环范围动态变化j <= i 使得每行列数等于行号
  2. 左对齐效果:第1行1列,第2行2列,形成左下三角
  3. 被乘数(j)在前 :每行是 1×i, 2×i, 3×i...
  4. 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 + '&times;' + 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 + '&times;' + 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
其他代码完全相同!
相关推荐
lihaozecq1 小时前
从零实现一个 ReAct Agent Loop - 可中断、可流式、多模型支持
前端·agent·ai编程
冴羽yayujs1 小时前
GitHub 前端热榜项目 - 日榜(2026-05-10)
前端·github
CAE虚拟与现实1 小时前
前后端调试常用工具大全
前端·后端·vue·react·angular
iuu_star1 小时前
跑通最简单的Vue3+Python前后端分离项目
前端·vue.js·python
AZaLEan__1 小时前
CSS3:从 2D 变换到 3D 翻转
前端·3d·css3
剑神一笑1 小时前
Linux du 命令深度解析:从磁盘占用统计到目录空间分析
linux·运维·前端
weixin_446260851 小时前
AI驱动的前沿前端技术栈深度解析:从模型能力到UI封装的完整生命周期
前端·人工智能·ui
程序猿编码1 小时前
Linux 高负载场景下 Web 服务访问日志极速定位工具实现解析(C/C++代码实现)
linux·服务器·c语言·前端·c++
ZC跨境爬虫1 小时前
跟着 MDN 学 HTML day_35:(深入解析 CharacterData 抽象接口)
java·前端·ui·html·edge浏览器·媒体