javascript.info 循环改写实战:把 for 循环替换为 while 循环(5-replace-for-while 任务详解)
javascript.info 循环改写实战:把 for 循环替换为 while 循环(5-replace-for-while 任务详解)
本文围绕 Modern JavaScript Tutorial(javascript.info)中 1-js/02-first-steps/13-while-for/5-replace-for-while/task.md 这道经典练习展开:在不改变程序行为的前提下,把一段 for 循环改写为等价的 while 循环。读完本文,你将掌握两种循环语句在结构与执行顺序上的对应关系,学会"行为不变"的循环等价改写方法,并能通过逐步追踪验证改写结果的正确性。该任务位于教程"First Steps"章节的 Loops: while and for 主题之下,是检验初学者是否真正理解循环三要素(初始化、条件、步进)的标准练习题。
任务要求:改写循环而保持行为不变
原任务(importance: 5,属于高优先级核心练习)要求如下:
Rewrite the code changing the
forloop towhilewithout altering its behavior (the output should stay same).
即:把下面的 for 循环改写为 while 循环,不得改变程序行为,最终输出必须与原代码完全一致:
for (let i = 0; i < 3; i++) {
alert( `number ${i}!` );
}
这段代码的运行效果是依次弹出三次对话框,内容分别为 number 0!、number 1!、number 2!。改写后的代码必须保持这三个输出值、输出顺序和输出次数不变。
先看懂 for 循环的三段式结构
要完成改写,首先要拆解 for 语句的三个组成部分。根据教程 Loops: while and for 中的说明,for 循环的标准语法为:
for (begin; condition; step) {
// ... loop body ...
}
对应到本题:
| 部分 | 本题代码 | 作用 |
|---|---|---|
| begin(初始化) | let i = 0 |
进入循环时仅执行一次,声明并初始化计数器 |
| condition(条件) | i < 3 |
每次迭代前检查;为假则循环终止 |
| body(循环体) | alert(number ${i}!) |
条件为真时反复执行 |
| step(步进) | i++ |
每次循环体执行完毕后运行 |
for 循环的完整执行算法可以展开为:
Run begin
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ ...
代入本题,逐步展开就是:
// for (let i = 0; i < 3; i++) alert(`number ${i}!`)
// run begin
let i = 0
// if condition → run body and run step
if (i < 3) { alert(`number 0!`); i++ } // i 变为 1
// if condition → run body and run step
if (i < 3) { alert(`number 1!`); i++ } // i 变为 2
// if condition → run body and run step
if (i < 3) { alert(`number 2!`); i++ } // i 变为 3
// ...finish, because now i == 3
可以看到,begin 只执行一次,之后是"条件检查 → 循环体 → 步进"的循环往复,直到 i 等于 3 时条件 i < 3 为假,循环结束。
改写为 while 循环:把三段式展开为显式语句
while 循环的语法只有"条件 + 循环体"两个要素:
while (condition) {
// code
// so-called "loop body"
}
改写思路很直接:把 for 的 begin 提前到循环外声明变量,把 condition 原样搬进 while 的条件位置,再把 step 移到循环体的末尾手动执行。官方参考答案位于 5-replace-for-while/solution.md:
let i = 0;
while (i < 3) {
alert( `number ${i}!` );
i++;
}
改写后的执行顺序与原 for 完全对应:
原 for 部分 |
while 改写位置 |
|---|---|
let i = 0(begin) |
循环外的 let i = 0; |
i < 3(condition) |
while (i < 3) 的条件 |
alert(...)(body) |
循环体内的第一条语句 |
i++(step) |
循环体末尾的 i++; |
改写后的循环同样依次弹出 number 0!、number 1!、number 2!,满足"输出保持不变"的要求。
为什么这样改写是正确的:执行语义等价性验证
while 版本与 for 版本在语义上等价,关键在于步进的执行时机。for 循环中 i++ 在每次循环体执行完之后运行,while 版本把 i++ 放在循环体末尾,同样保证了:
- 首次进入循环时
i = 0,条件0 < 3为真,弹出number 0!,随后i++使i = 1; - 第二次检查
1 < 3为真,弹出number 1!,i++使i = 2; - 第三次检查
2 < 3为真,弹出number 2!,i++使i = 3; - 第四次检查
3 < 3为假,循环终止。
把执行过程"展开"验证如下:
let i = 0;
// if (0 < 3) { alert(`number 0!`); i++ }
// if (1 < 3) { alert(`number 1!`); i++ }
// if (2 < 3) { alert(`number 2!`); i++ }
// if (3 < 3) → false, loop stops
如果漏写循环体末尾的 i++,循环条件永远为真,会形成无限循环——正如教程中特别提醒的:在浏览器环境中无限循环可以被用户强制停止,在服务端 JavaScript 中则只能终止进程。因此,把 step 从 for 头部搬到 while 循环体末尾,是改写中最容易遗漏、也最关键的一步。
改写中的细节要点
1. 计数器变量的作用域差异
for 中的 let i = 0 是"内联声明",变量 i 只在循环内部可见;循环结束后访问 i 会报错(ReferenceError)。而改写后的 while 版本把 i 声明在循环外部,循环结束后 i 依然存在,且值为 3。如果原 for 代码依赖内联变量的块级作用域(例如循环后有同名变量声明),改写时需要留意作用域变化;如果原代码使用的是循环外已存在的变量(如 for (i = 0; i < 3; i++)),则改写前后作用域一致。
2. for 与 while 的结构关系
教程指出,去掉 for 的 begin 和 step 之后,for (; i < 3;) { ... } 与 while (i < 3) { ... } 完全等价。这从侧面印证了:任何 for 循环都可以在不改变条件检查语义的前提下改写为 while,只需把三段内容拆开安置。反过来,while 循环也可写成省略 begin/step 的 for 形式。
3. 不要混淆 do..while
教程中的第三种循环 do..while 把条件检查放在循环体之后,保证循环体至少执行一次。本题要求"行为不变",因此必须使用先检查条件的 while 形式,而不能使用 do..while——后者在原 for 条件一开始就为假的情况下仍会执行一次循环体,行为不再等价。
4. 条件与递增的执行顺序
改写时注意保持"先检查、后递增"的顺序:while 的条件中不要直接写 i++(如 while (i++ < 3)),因为后缀自增会先返回旧值再递增,导致输出结果偏移。本题标准解法把递增放在循环体末尾,清晰还原了 for 中"body 之后执行 step"的顺序,这是保持输出一致最稳妥的写法。
相关练习题与延伸阅读
本节所属的 13-while-for 目录下还有一系列配套练习,可用来巩固循环语义:
- 1-loop-last-value:分析
while (i)配合alert(i--)时最后一次弹出的值(答案为1),训练对真值转换与后缀自增的理解; - 2-which-value-while:对比
while (++i < 5)与while (i++ < 5)输出序列的差异(分别输出 1~4 与 1~5),演示前后缀形式在条件表达式中的不同行为; - 3-which-value-for:证明
for的 step 中i++与++i输出相同(均为 0~4),因为步进与条件检查分离,其返回值未被使用; - 4-for-even:用
for配合取模运算符%输出 2 到 10 的偶数; - 6-repeat-until-correct 与 7-list-primes:进一步练习循环嵌套与终止条件设计。
小结
"把 for 改写为 while"表面上是一次机械的语法搬运,实际上考察的是对循环执行模型的理解:for 的 begin / condition / step / body 四要素如何按固定顺序反复执行。改写步骤可归纳为三条:
- 将
begin提取为循环前的变量声明; - 将
condition原样放入while条件; - 将
step移入循环体末尾,且顺序必须在body之后。
遵循这三步即可保证输出不变,这也是本任务要求"without altering its behavior"的判定标准。读者可在浏览器控制台分别运行 task.md 与 solution.md 中的代码,逐一核对弹出的三次 alert 内容来验证等价性。