主题
循环语句与跳转语句(Day4 2/3)
本页对应 Day04_JavaScript,共 6 段视频,约 1 小时 20 分。
学习目标
- 能根据给定需求编写 do-while 循环,并说明其“先执行一次循环体,再判断条件”的执行流程。
- 能对比 while 与 do-while 在首次条件不成立、正常循环、可能形成死循环等场景下的执行结果差异。
- 能使用 for 循环实现计数类需求,并准确说明初始化、条件判断、变量变化三部分的执行时机。
- 能识别并改写特殊结构的 for 循环,包括省略初始化或变化表达式、使用逗号表达式声明多个循环变量,并解释其运行结果。
- 能使用不依赖固定循环次数的循环结构解决随机数取值等问题,并正确设计继续循环与结束循环的条件。
- 能使用循环嵌套输出指定行数和列数的表格,并解释内外层循环变量在每轮执行中的取值变化。
知识点
材料说明
本节笔记提纲是“1 回顾”,列出单向分支、双向分支、多向分支 else if、多向分支 switch case、嵌套;但所附转写稿实际讲的是 do-while、while 与 do-while 区别、for、特殊 for、无标记变量循环、循环嵌套。两者主题不一致。为避免把循环内容硬套进分支回顾,下面按笔记提纲扩写分支结构回顾;转写稿中的循环推导不强行混入。笔记未附图片和代码,故无图片与代码可保留。转写稿中可识别的同音字如 where→while、dowhere→do while、负循环→for 循环、虚化→循环、conl log→console.log、domentwright→document.write、提包→tbody、rose→rows、cos→cols、is nine→isNaN 等,已按语义校正,但因主题不一致不展开。
单向分支:if
为什么需要:程序有时只在条件成立时做一件事,条件不成立就跳过。比如只有满足某个判断才执行输出或赋值。
怎么想:先问自己“条件不成立时要不要做别的?”如果不需要,就用单向分支。不要把单向分支写成空 else,空 else 只会增加阅读负担。
怎么做:语法示意为 if (条件) { 语句 }。条件表达式要能明确得到真/假;大括号内可以写一条或多条语句。回顾时重点看条件是否互斥、是否漏掉边界。
易错点:把 == 写成 =;省略大括号后误以为多条语句都受 if 控制;条件表达式依赖隐式转换时没有写清楚;在 if 后误加分号,导致分支体变成空语句。
双向分支:if...else
为什么需要:条件成立走一条路,不成立走另一条路,两条路径互斥。它比“两个独立 if”更清楚,因为能明确表达“非此即彼”。
怎么想:先确认两条路径是否真的互斥。如果两个条件可能同时成立,就不应写成 if...else,而应分别判断。回顾时用“成立做什么、不成立做什么”两句话复述逻辑。
怎么做:语法示意为 if (条件) { A } else { B }。A 与 B 只执行其中一个。调试时可在 A、B 入口各放一个日志,确认实际走的是哪条路径。
易错点:else 悬挂问题——else 只与最近的未配对 if 结合;在 else 后误加分号;把本应互斥的条件写成两个独立 if,造成两条路径都执行。
多向分支:else if
为什么需要:当判断不止两种结果,而是多个区间或多个条件时,需要按顺序筛选。它适合“分数段”“等级段”这类连续区间判断。
怎么想:先把所有条件列出来,检查是否互斥、是否有覆盖不到的情况。顺序很重要:范围窄的条件放前面,范围宽的条件放后面,否则宽条件会“截胡”。
怎么做:语法示意为 if (条件1) { ... } else if (条件2) { ... } else { ... }。最后一个 else 常用来兜底。回顾时用边界值代入,例如最小值、最大值、临界值。
易错点:区间重叠导致后面的分支永远进不去;漏掉 else 兜底;条件顺序写反;把 else if 写成独立的 if,导致多个分支连续判断。
多向分支:switch case
为什么需要:当一个表达式要与多个固定值做等值匹配时,switch 比一长串 else if 更整齐。它适合菜单编号、状态码这类离散值。
怎么想:先判断是“等值匹配”还是“区间判断”。等值匹配可用 switch;区间判断仍用 else if。还要确认每个 case 后是否要跳出。
怎么做:语法示意为 switch (表达式) { case 值: 语句; break; default: 语句; }。每个 case 后通常写 break,default 处理未匹配情况。调试时重点看是否发生 case 贯穿。
易错点:忘记 break 导致继续执行下一个 case;case 后写成条件表达式而不是固定值;default 位置随意导致阅读混乱;表达式类型与 case 值类型不一致。
嵌套
为什么需要:一个分支里还需要再分类时,就要嵌套。比如先判断类型,再判断该类型下的具体状态。嵌套能表达层级,但也会增加阅读成本。
怎么想:先写外层分类,再写内层细分;每层只解决一个问题。如果嵌套超过两三层,应考虑提前 return、拆函数或改用更扁平的条件。
怎么做:在 if 或 switch 的分支体里再写 if/switch。缩进要清楚,else 要与对应 if 对齐。回顾时用“外层不成立时内层是否还执行”来检查逻辑。
易错点:else 与最近 if 配对导致逻辑错位;缩进混乱看不出层级;嵌套过深;内外层条件重复判断;忘记某一层不成立时是否应直接结束。
do-while:先执行一次,再决定是否继续
为什么需要:有些任务必须先做一次,再判断结果是否满足要求;例如先取一个随机数,再看它是否达标。
怎么想:do-while 的结构是 do { 循环体 } while (条件);。它第一次不判断,直接执行循环体;从第二次开始,每次都要先判断条件,成立才继续。转写稿里强调:不要被“先执行后判断”误导成“以后都不判断”,以后每次仍要判断。
怎么做:
js
let i = 0;
do {
console.log("高尔乐今天吃了" + i + "个韭菜包子");
i++;
} while (i < 10);循环结束后 i 是 10,因为 i 为 9 时条件成立,执行后自增为 10,再判断不成立而结束。
易错点:do-while 至少执行一次;循环结束后的标记变量通常定格在“让条件不成立”的值,如 7 到 15 输出后是 16,56 到 43 倒序输出后是 42。
while 与 do-while 的区别:差别小,但可能“差之千里”
为什么需要:理解二者区别,才能判断“第一次是否必须执行”。
怎么想:正常循环次数超过一次时,二者初始值、条件、变量变化可以完全一样;区别只在第一次:while 先判断,do-while 先执行。
怎么做:转写稿用 n = 10; while(n > 1) 时一次都不执行;换成 do { ... } while(n > 1) 且循环体里 n++,就会变成死循环,因为第一次不判断,之后 n 越来越大,条件永远成立。
易错点:不要只背“先判断/先执行”,要按步骤推:第一次是否判断?循环体是否改变条件?变量最终是多少?
for 循环:把三件事写在一起
为什么需要:循环通常都需要初始化、条件判断、变量变化。for 把这三部分集中到小括号里,结构更精炼,也是最常用的循环。
怎么想:for(初始化; 条件; 变化) 中,初始化只在循环开始前执行一次;条件每次先判断;变化在循环体语句之后执行。它和 while 的执行逻辑一致,条件第一次不成立就一次都不执行。
怎么做:
js
for (let i = 0; i < 10; i++) {
console.log("高尔乐今天吃了" + i + "个韭菜包子");
}7 到 15 可写 for(let n=7; n<=15; n++);倒序可写 for(let i=56; i>=43; i--)。
易错点:循环结束后 i 是 10,不是 9;因为 9 成立并执行后还会自增。不要以为初始化每次循环都执行,否则会死循环。
特殊结构的 for:理解执行顺序,不主动炫技
为什么需要:特殊写法能加深对 for 三部分执行时机的理解。
怎么想:初始化可以写到外面,但小括号里要用分号占位;变化可以写进大括号,但必须放在循环体语句之后,否则第一次输出值会变。
怎么做:
js
let i = 1;
for (; i < 10; i += 2) {
console.log(i);
}转写稿还演示了逗号运算符:for(let x=1,y=1; x<=15,y<=10; x++,y++)。这里条件实际取逗号表达式最后一个操作数 y<=10,x 不决定循环。
易错点:特殊写法容易漏分号、写错 +=;实际开发仍推荐标准结构。
不使用标记变量的循环:用结果决定是否继续
为什么需要:有些循环次数无法预先知道,只知道“结果不达标就继续取”。
怎么想:用 Math.random() 取 0 到 1 之间的随机数;若结果不满足要求,就继续循环,直到满足为止。
怎么做:
js
let rnd = Math.random();
while (rnd <= 0.99) {
rnd = Math.random();
}
console.log(rnd);用 do-while 可先取再判断:
js
let rnd;
do {
rnd = Math.random();
} while (rnd >= 0.001);
console.log(rnd);易错点:转写稿里的“max点round”“ron点一样”是同音误写,实际是 Math.random()。若用 for 写条件范围,初始化只执行一次,必须在“变化”部分重新取随机数,否则可能死循环。
循环嵌套:外层管行,内层管列
为什么需要:输出表格、矩阵等结构时,需要“行里再循环列”。
怎么想:外层循环控制行,内层循环控制每行的单元格。内外层变量不能同名;否则内层会把外层变量改掉。
怎么做:
js
document.write("<table>");
for (let i = 0; i < 10; i++) {
document.write("<tr>");
for (let j = 0; j < 8; j++) {
document.write("<td>" + i + "-" + j + "</td>");
}
document.write("</tr>");
}
document.write("</table>");易错点:document.write 写标签会按 HTML 解析;页面换行要靠标签,不像控制台 console.log 自动换行。每行内层 j 都从 0 开始,所以第一行是 0 到 7,第二行仍是 0 到 7;而 i 在该行 8 个单元格中保持不变。
先理解“跳转”:改变默认执行顺序
转写稿里反复演示 while、do-while、for 的执行顺序:初始化、条件判断、循环体、变量变化,一步一步按顺序走。跳转语句的作用,就是在这个默认顺序上开两个“口子”:break 是提前结束所在结构,continue 是跳过本次循环的剩余部分。转写稿中“度/都尔”应理解为 do-while,“威尔”指 while;这些循环的执行顺序,正好帮助我们判断跳转语句该放在哪里。
break:结束所在的结构
本节笔记给它的结论是:
1. 在 switch case 中使用: 结束所在的 case
2. 在循环中使用: 结束循环,一旦执行到break,结束整个循环为什么需要它?因为很多场景并不想等条件自然不成立。比如 switch 中某个 case 已经处理完,如果继续往下执行,就会“穿透”到下一个 case;循环中已经找到目标,也没必要继续跑完剩余次数。
怎么想?把 break 理解成“当前这一层的出口”,不是“整个程序结束”。怎么做?在 switch 的每个 case 末尾写 break;在循环体里满足某个条件时写 break。例如循环中一旦命中目标就立刻退出。
易错点:break 只结束它所在的那一层循环或 switch。循环嵌套时,内层 break 不会顺手结束外层。转写稿讲嵌套循环时特别强调内外层变量不能重名,否则内层会把外层循环标记变量改乱;同理,break 也要看清自己写在哪一层。
continue:跳过本次,不结束循环
笔记结论是:
在循环中使用: 跳出本次循环,一旦执行到continue,本次循环中后面语句不再执行,下一次循环继续为什么需要它?有些数据不需要处理,但循环还要继续。比如只处理满足条件的项,其他项跳过即可。continue 和 break 最大的区别是:break 让循环结束,continue 只让“这一次”提前收尾。
怎么想?执行到 continue 后,本次循环中它后面的语句不再执行,然后进入下一次循环流程。怎么做?把“不满足条件就跳过”的判断放在循环体前部,满足跳过条件时 continue。
易错点:在 for 循环中,按转写稿的分析,初始化只执行一次,条件每次先判断,循环标记变量变化在循环体之后执行;因此 continue 跳过的是本次循环体剩余语句,不是把整个循环废掉。更危险的是 while/do-while:转写稿里演示过 while 忘记 i++ 导致页面卡死,这说明循环必须有退出机制。若把变量更新写在 continue 后面,可能每次都被跳过,最后变成死循环。所以使用 continue 时,要检查“更新变量”是否会被跳过。
用执行顺序定位跳转语句
转写稿讲 for 时有一个很实用的推导:for 小括号里的初始化只执行一次,条件表达式每次循环前判断,变量变化在循环体语句之后执行。把这个顺序记住,break 和 continue 的位置就不容易放错。循环嵌套时,外层每走一次,内层要完整走一轮;内层变量名要和外层区分开。跳转语句也一样,写在内层只影响内层。
小结检查清单
看到 break,先问:它结束的是 switch 还是循环?是哪一层?看到 continue,先问:本次剩余语句有哪些?循环变量还会不会更新?只要把这两个问题问清楚,跳转语句就不会和普通顺序执行混在一起。
课堂画面






代码示例
示例 1:do-while 循环完成多个案例
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>do-while 循环</title>
</head>
<body>
<script>
// 1. 输出 0~9 个韭菜包子
let i = 0;
do {
console.log('高尔乐今天吃了' + i + '个韭菜馅的包子');
i++;
} while (i < 10);
console.log('循环结束后 i =', i); // 10
// 2. 输出 7~15
let j = 7;
do {
console.log(j);
j++;
} while (j <= 15);
console.log('循环结束后 j =', j); // 16
// 3. 倒序输出 56~43
let k = 56;
do {
console.log(k);
k--;
} while (k >= 43);
console.log('循环结束后 k =', k); // 42
// 4. 输出 10 以内所有偶数
let m = 0;
do {
console.log(m);
m += 2;
} while (m <= 10);
console.log('循环结束后 m =', m); // 12
// 5. 100 以内所有数字的和
let sum = 0;
let n = 1;
do {
sum += n;
n++;
} while (n <= 100);
console.log('100以内所有数字的和 =', sum); // 5050
console.log('循环结束后 n =', n); // 101
// 6. 10 以内所有奇数的乘积
let res = 1;
let p = 1;
do {
res *= p;
p += 2;
} while (p <= 10);
console.log('10以内所有奇数的乘积 =', res); // 945
console.log('循环结束后 p =', p); // 11
</script>
</body>
</html>关键行与易错点:
do { ... } while (条件);的特点是先执行一次循环体,再判断条件。while后面的条件表达式必须写,且循环体中一定要让循环标记变量变化,例如i++、m += 2、p += 2,否则容易死循环。- 循环结束后,循环标记变量通常定格在“让条件不成立”的值,例如
i < 10结束后i是10,j <= 15结束后j是16。 - 偶数案例中使用
m += 2,条件写成m <= 10,会输出0、2、4、6、8、10,结束后m为12。 - 奇数乘积案例中
res要初始化为1,不能初始化为0,否则乘积永远是0。
示例 2:while 与 do-while 的区别
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>while 与 do-while 的区别</title>
</head>
<body>
<script>
// 常规情况:循环次数超过一次时,两者结果基本一样
let i = 0;
while (i < 5) {
console.log('while:', i);
i++;
}
console.log('while 结束后 i =', i); // 5
let j = 0;
do {
console.log('do-while:', j);
j++;
} while (j < 5);
console.log('do-while 结束后 j =', j); // 5
// 特殊情况:第一次条件就不成立
let n = 10;
while (n < 10) {
console.log('while 执行了');
n++;
}
console.log('while 结束后 n =', n); // 10,一次都没执行
let m = 10;
do {
console.log('do-while 执行了');
m++;
} while (m < 10);
console.log('do-while 结束后 m =', m); // 11,执行了一次
</script>
</body>
</html>关键行与易错点:
while是“先判断,再执行”;do-while是“先执行一次,再判断”。- 常规循环次数超过一次时,二者在循环标记变量初始值、条件表达式、变量变化方式相同的情况下,结果基本一致。
- 真正的区别体现在第一次:如果初始条件不成立,
while一次都不执行,do-while至少执行一次。 - 不要把
do-while写成条件永远成立的形式,例如转写稿中N从10开始、条件写成N > 1,do-while会变成死循环。 do-while后面的while (条件);末尾分号不要漏写。
示例 3:for 循环嵌套输出表格
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>for 循环嵌套输出表格</title>
<style>
table {
width: 800px;
table-layout: fixed;
border-collapse: collapse;
}
td {
border: 1px solid #999;
padding: 5px;
}
</style>
</head>
<body>
<script>
// 基础 for 循环:输出 0~9
for (let i = 0; i < 10; i++) {
console.log('高尔乐今天吃了' + i + '个韭菜馅的包子');
}
// 循环结束后 i 为 10,是让条件不成立的值
// 嵌套 for:输出 rows 行 cols 列的表格
let rows = 10;
let cols = 8;
document.write('<table>');
document.write('<tbody>');
for (let i = 0; i < rows; i++) {
document.write('<tr>');
for (let g = 0; g < cols; g++) {
document.write('<td>hello</td>');
}
document.write('</tr>');
}
document.write('</tbody>');
document.write('</table>');
</script>
</body>
</html>关键行与易错点:
for (初始化; 条件; 变化)中,初始化只执行一次;条件每次循环前判断;变化在循环体执行之后执行。for循环的条件如果一开始就不成立,循环体一次都不执行,这一点和while一样。- 嵌套循环时,内外层循环变量不能同名。外层用
i,内层要用g这类不同变量,否则内层会改掉外层变量,导致循环对不上号。 document.write是往文档流里写内容,输出表格标签时要按顺序成对写出<table>、<tbody>、<tr>、<td>。table-layout: fixed;用于固定列宽,border-collapse: collapse;用于合并相邻单元格边框。- 可以把
rows和cols改成变量或用户输入,从而动态控制表格行数和列数。
常见错误
1. do-while 的 while(条件) 后面漏分号
- 现象:编辑器飘红报语法错误,或代码"看着对"却跑不起来。
- 原因:
do…while(条件);整体是一条语句,结尾必须有分号;而do{}的大括号后面反而不能加分号。 - 修法:记住口诀"大括号后不加、while 小括号后必加",写成
do{ … } while(cond);。
2. 循环变量忘了变化,或变化写了两遍
- 现象:前者页面直接卡死、控制台疯狂刷屏;后者输出"跳着走"、次数少一半。
- 原因:变量不变 → 条件恒成立 → 死循环;
for第三部分已经写了i++,循环体里又写一次 → 一轮变两次。 - 修法:写完循环立刻回头确认"变量在哪变、变了几次";
for的变化只写在第三部分,循环体内不再重复。
3. 嵌套循环内外层用了同一个变量名 i
- 现象:外层明明该跑 10 次却只跑 1 次,内层输出也不对。
- 原因:内层循环结束时把
i改成了终止值,外层再判断时条件已不成立(顺序结构里两个循环用同名i没事,是因为前一个已经跑完)。 - 修法:内层换名,如外层
i、内层j;并养成"嵌套必换名"的习惯。
4. 以为 for 里用逗号连接的两个条件都生效
- 现象:
for(x=1, y=1; x<=15, y<=10; x++, y++)只按y控制,x<=15形同虚设。 - 原因:逗号表达式取最后一个操作数的值,前面的条件被丢弃。
- 修法:要同时成立用
&&;只是想让两个变量一起变,才用逗号。
5. "取随机数直到满足条件"的循环写成死循环
- 现象:
for或do-while里取随机数,程序永远出不来。 - 原因:取随机数的语句只写在
for的初始化部分(只执行一次)或写在do外面,循环体内没有重新取值,变量永远不变。 - 修法:把"重新取值"放进循环体内,每轮都取一次;这类场景
do-while最自然——第一次不用判断,取值直接写在do{}里。
6. 混淆 break 与 continue,或在嵌套中误判作用范围
- 现象:本想跳过某一次,结果整个循环结束了;或以为
break能跳出两层。 - 原因:
break结束整个(最近一层)循环,continue只结束本次;两者都只作用于最近的一层。 - 修法:按语义选词;需要跳出多层时用标志变量或把逻辑抽成函数
return。
7. 误判循环结束后循环变量的值
- 现象:循环输出到 9,就以为结束后
i是 9,结果拿到 10。 - 原因:最后一次条件成立后又执行了一次变化,变量定格在"让条件第一次不成立"的那个值。
- 修法:需要"最后一个有效值"时,在循环体内另存一份,不要直接用循环变量。
练习
练习 1(三循环互译) 分别用 while、do-while、for 输出 1~100 中所有"能被 3 整除但不能被 5 整除"的数,并统计个数。三次结果必须完全一致,写完后对比三种写法里"初始化 / 条件 / 变化"分别落在什么位置。
练习 2(do-while 的用武之地) 反复调用 Math.random() 取随机数,直到取到大于 0.99 的数为止,输出这个数以及一共取了多少次。要求:分别用 for 和 do-while 各写一遍,比较哪种写法不用在循环外先取一次值。
练习 3(次数未知 + 区间条件) 取一个大于 0.59 且小于 0.61 的随机数,取不到就继续取。提示:循环条件要写"不满足期望"的那一面,用 || 连接两个越界判断;注意循环体内必须重新取值。
练习 4(break / continue) ① 在 100~200 之间找出第一个能被 7 整除的数,找到即停止(用 break)。 ② 输出 1~50 中所有不是 3 的倍数的数(用 continue)。 ③ 思考:如果把 ① 改成"找出前 3 个",break 还够用吗?
练习 5(嵌套 + 用户输入) 用双层循环在页面输出表格:行数、列数由 prompt 输入,非数字时按 0 处理;每个单元格显示"行号-列号"(从 1 开始)。做完后改一改:把内层单元格显示成外层行号,观察每一行有什么不同,说清楚为什么。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | do-while循环 | 14:56 |
| 2 | while和do while区别 | 08:32 |
| 3 | for循环 | 17:20 |
| 4 | 特殊结构的for循环 | 07:02 |
| 5 | 不使用标记变量的循环 | 16:37 |
| 6 | 循环嵌套 | 16:29 |
小结
while、do-while、for能互相改写,选哪个不取决于"哪个高级",而取决于"最少执行几次"和"变量该在哪一层声明"。do-while的真正价值只有一个场景:第一次判断没有意义、必须先做一次(典型如"先取一次随机数再判断")。其余情况用它只是给自己加风险。- 写循环前先回答三问:初始值是什么、什么时候停、每轮怎么变。三个答案里缺一个,代码里就埋了一个死循环。
for的三部分执行时机要能背下来:初始化只一次、条件每轮先判断、变化在循环体之后。判断不出变化的位置时,就看"第一次进循环体时变量是几"。- 嵌套循环的心智模型是"外层走一步,内层跑一整轮";内外层变量同名是隐性 bug,不是风格问题。
- 循环次数由运行结果决定时(随机数、查找、用户输入),条件应该写"还不满足期望"的那一面,而不是"满足期望"的那一面。
break/continue的作用范围永远只有最近一层,多层跳出一律靠标志变量或函数封装。- 循环变量在结束后定格于"让条件第一次不成立"的值——它通常比最后一个有效值多走了一步,别拿它当结果用。