引言
理论知识的学习为我们搭建了理解循环的框架,但真正的编程能力需要在解决具体问题的过程中锤炼。今天,我们进入循环的技能测试环节,通过三道精心设计的题目来检验你是否真正掌握了for、while和do...while循环的用法,以及break和continue语句在循环控制中的作用。
这些题目不仅考察循环本身,还会涉及 DOM 操作、数组遍历、对象属性访问以及条件判断等综合知识。三道题目各自要求使用不同的循环类型,这种设计迫使我们必须灵活切换思维模式,而不是机械地套用一种结构。让我们逐一拆解这些题目,在实战中巩固循环的运用能力。
一、实用的 DOM 操作
在开始解题之前,我们需要了解一个贯穿三道题目的基础技能,那就是使用 JavaScript动态创建和操作 HTML 元素。虽然本课程尚未系统地讲解 DOM 操作,但这三道题目都要求我们通过 JavaScript 来创建新元素、设置其内容并将其添加到页面中。
三个核心 API
| API | 作用 | 示例 |
|---|---|---|
document.createElement() | 创建一个新的 HTML 元素 | document.createElement('li') |
element.textContent | 设置或获取元素的文本内容 | listItem.textContent = 'tomatoes' |
parent.appendChild() | 将子元素追加到父元素中 | list.appendChild(listItem) |
createElement()的参数是标签名称的字符串:'ul'创建无序列表、'li'创建列表项、'p'创建段落;- 创建出来的元素需要通过
appendChild()方法添加到页面中某个已有的父元素里,才能最终显示在屏幕上; - 元素内部的文本内容通过
textContent属性来设置。
这三个 API 组合起来,就构成了用 JavaScript 动态生成页面内容的最小工具集。在第一题中,我们需要创建多个列表项并将它们逐个追加到列表中;在第二题中,我们需要将搜索结果写入一个段落并追加到展示区域;第三题同样需要将找到的质数结果写入段落。理解这个基本的 DOM 操作流程是顺利解答三道题目的前提。
二、循环 1:遍历数组并生成列表
第一道题目为我们设定了这样一个场景:有一个名为myArray的数组,里面存储了五种食材的字符串,包括西红柿、鹰嘴豆、洋葱、大米和黑豆。同时,起始代码已经帮我们创建好了一个空的无序列表元素list,我们可以在 JavaScript 中直接使用它。
2.1 任务目标
编写一个循环,遍历myArray中的每一个元素,完成以下操作:
- 为每个元素创建一个列表项(
<li>元素); - 将当前数组元素的值赋给列表项的文本内容;
- 将这个列表项追加到
list无序列表中。
最终,list会被起始代码自动添加到页面的展示区域。
constmyArray=['tomatoes','chick peas','onions','rice','black beans'];constlist=document.createElement('ul');// Add your code here// Don't edit the code below here!constsection=document.querySelector('section');section.appendChild(list);2.2 解题思路与代码实现
这道题考察的是最基本的for循环配合数组遍历的模式:
for(leti=0;i<myArray.length;i++){constlistItem=document.createElement('li');listItem.textContent=myArray[i];list.appendChild(listItem);}执行流程:
| 迭代 | i | myArray[i] | 操作 |
|---|---|---|---|
| 第1次 | 0 | 'tomatoes' | 创建<li>tomatoes</li>并追加到<ul> |
| 第2次 | 1 | 'chick peas' | 创建<li>chick peas</li>并追加 |
| 第3次 | 2 | 'onions' | 创建<li>onions</li>并追加 |
| 第4次 | 3 | 'rice' | 创建<li>rice</li>并追加 |
| 第5次 | 4 | 'black beans' | 创建<li>black beans</li>并追加 |
| 终止 | 5 | — | 5 < 5为false,循环结束 |
DOM 操作三步走:
createElement('li') → textContent = myArray[i] → list.appendChild(listItem) 创建元素 设置文本内容 追加到父元素这道题虽然简单,但它完美地展示了循环在动态生成页面内容时的核心作用——我们不需要手动编写五个<li>标签,只需要一个循环就能处理任意长度的数组。如果将来食材列表扩展到五十种,代码依然只有这几行,完全不需要改动。这就是循环所带来的可扩展性的力量。
三、循环 2:搜索电话簿并提前退出
第二道题目将我们带入了一个更贴近实际应用的场景。我们有一个电话簿数组phonebook,其中每个元素都是一个对象,包含name和number两个属性。题目要求使用一种在上一道任务中没有使用过的循环类型(即不能用普通for循环)。
3.1 任务目标
在电话簿中搜索给定的名字('Mustafa'),如果找到了匹配的联系人:
- 将该联系人的名字和电话号码输出到段落
para中; - 使用
break语句立即退出循环,不再继续搜索后续条目。
constname='Mustafa';constpara=document.createElement('p');constphonebook=[{name:'Chris',number:'1549'},{name:'Li Kang',number:'9634'},{name:'Anne',number:'9065'},{name:'Francesca',number:'3001'},{name:'Mustafa',number:'6888'},{name:'Tina',number:'4312'},{name:'Bert',number:'7780'},{name:'Jada',number:'2282'},]// Add your code here// Don't edit the code below here!constsection=document.querySelector('section');section.appendChild(para);3.2 解题思路与代码实现
既然不能用for循环,自然的选择就是while循环:
leti=0;while(i<phonebook.length){if(phonebook[i].name===name){para.textContent=`${phonebook[i].name}'s number is${phonebook[i].number}.`;break;}i++;}while循环三要素的分布:
| 要素 | 在代码中的位置 | 具体写法 |
|---|---|---|
| 初始化器 | 循环之前 | let i = 0 |
| 退出条件 | while括号内 | i < phonebook.length |
| 最终表达式 | 循环体末尾 | i++ |
执行流程:
i=0 → Chris ≠ Mustafa → i++ → 继续 i=1 → Li Kang ≠ Mustafa → i++ → 继续 i=2 → Anne ≠ Mustafa → i++ → 继续 i=3 → Francesca≠ Mustafa → i++ → 继续 i=4 → Mustafa = Mustafa → 输出结果 → break → 循环终止! i=5 → (永远不会到达,已经被 break 终止)3.3 关键细节:break 的价值
如果没有break语句,即使已经找到了 Mustafa,循环仍然会继续遍历后面的 Tina、Bert 和 Jada,做无意义的检查。对于只有 8 条记录的电话簿来说这无关紧要,但如果电话簿有 10000 条记录而目标恰好在前 10 条,break就能节省 99.9% 的循环时间。
这道题的核心价值在于让我们体会while循环与break语句配合使用的典型场景:处理一个需要提前退出的线性查找任务。while循环将计数器的管理逻辑分散开来,在某些场景下这种分散反而让搜索逻辑更加清晰。
四、循环 3:倒序筛选质数
第三道题目在逻辑上最为复杂,它融合了倒序遍历、条件过滤以及continue语句的运用。题目要求使用一种在前两个任务中没有使用过的循环类型,排除了for和while。
4.1 任务目标
- 变量
i初始值为500,被明确指定为迭代器; - 变量
para是一个已创建好的段落元素,用于输出结果; - 函数
isPrime(num)用于判断传入的数字是否为质数(是质数返回true,不是则返回false);
任务要求:
- 使用循环倒序(从大到小)遍历从 2 到 500 的所有数字;
- 对于每个数字,调用
isPrime()进行检测; - 如果不是质数,使用
continue跳过当前迭代,不执行输出; - 如果是质数,则将该数字追加到
para.textContent中,用分隔符隔开。
质数:大于 1 且只能被 1 和自身整除的自然数(1 不算质数)。
leti=500;constpara=document.createElement('p');functionisPrime(num){for(leti=2;i<num;i++){if(num%i===0){returnfalse;}}returntrue;}// Add your code here// Don't edit the code below here!constsection=document.querySelector('section');section.appendChild(para);4.2 解题思路与代码实现
既然for和while都已经被前两道题使用过了,剩下的选择就是do...while循环:
do{if(!isPrime(i)){i--;continue;}para.textContent+=`${i},`;i--;}while(i>=2);do...while循环三要素的分布:
| 要素 | 在代码中的位置 | 具体写法 |
|---|---|---|
| 初始化器 | 循环之前 | let i = 500(题目已提供) |
| 退出条件 | while括号内(循环末尾) | i >= 2 |
| 最终表达式 | 循环体末尾 | i--(出现在两处) |
执行流程图解:
i=500 → 进入循环体 ├─ isPrime(500)? → false → i-- (i=499) → continue → 回到 while 检查 ├─ i=499 → isPrime(499)? → true → 追加 "499, " → i-- (i=498) ├─ i=498 → isPrime(498)? → false → i-- (i=497) → continue ├─ i=497 → isPrime(497)? → ... └─ ... 重复直到 i=1 → 退出条件 i >= 2 为 false → 循环终止4.3 ⚠️ 关键陷阱:continue 与计数器的顺序
这道题最易出错的地方在于continue语句的使用位置。看下面的对比:
// ❌ 错误!会导致无限循环do{if(!isPrime(i)){continue;// 直接跳到 while 检查,i 没有递减!}para.textContent+=`${i},`;i--;}while(i>=2);// ✅ 正确:在 continue 之前先递减 ido{if(!isPrime(i)){i--;// 先递减计数器continue;// 再跳过本次迭代}para.textContent+=`${i},`;i--;}while(i>=2);原理:continue会跳过循环体内剩余的代码,直接跳转到while条件检查。如果在continue之前没有更新i,i将永远不变,退出条件永远无法满足,从而导致无限循环。这是一个非常常见的陷阱,务必牢记:
在循环中使用
continue时,必须确保迭代器在continue之前已经更新。
五、三道题目综合对比
| 对比维度 | 循环 1 | 循环 2 | 循环 3 |
|---|---|---|---|
| 循环类型 | for | while | do...while |
| 遍历方向 | 正序(0 → n) | 正序(0 → n) | 倒序(500 → 2) |
| 循环控制 | 自然结束 | break提前退出 | continue跳过迭代 |
| 数据结构 | 字符串数组 | 对象数组 | 数值 + 函数判断 |
| DOM 操作 | 创建多个<li> | 写入单个<p> | 拼接写入<p> |
| 核心考点 | 基本遍历 + 动态生成 | 线性查找 + 提前退出 | 条件过滤 + 倒序递减 |
六、常见错误与避坑指南
6.1 忘记初始化计数器
// ❌ 错误while(i<phonebook.length){...}// i 未定义!// ✅ 正确leti=0;while(i<phonebook.length){...}6.2 忘记在循环体中更新计数器
// ❌ 错误:无限循环!leti=0;while(i<5){console.log(i);// 忘记 i++!}// ✅ 正确leti=0;while(i<5){console.log(i);i++;}6.3 退出条件写反
// ❌ 错误:循环一次都不执行do{para.textContent+=i;i--;}while(i<=2);// i 初始为 500,条件立假// ✅ 正确do{para.textContent+=i;i--;}while(i>=2);总结
完成这三道技能测试题,我们经历了一次从基础到进阶的循环实战训练。
| 题目 | 循环类型 | 核心技能 | 关键收获 |
|---|---|---|---|
| 生成食材列表 | for | 遍历数组 + DOM 动态创建 | 循环的可扩展性——代码行数与数据规模解耦 |
| 搜索电话簿 | while | 对象属性访问 +break | 线性查找中提前退出的性能优化模式 |
| 筛选质数 | do...while | 倒序遍历 +continue | continue前必须更新计数器,避免无限循环 |
三道题目覆盖了三种不同的循环结构,每一种都在特定的场景下展现出其独特的价值。更重要的是,它们共同揭示了循环编程中必须时刻留意的核心原则:
- 清晰的初始化——计数器从哪开始?
- 正确的退出条件——什么时候停止?边界情况对吗?
- 可靠的迭代器更新——每次迭代是否都向退出条件靠近?
当你能够根据问题的特性自如地选择最合适的循环类型,并正确处理提前退出(break)与跳过迭代(continue)的逻辑时,你就真正掌握了 JavaScript 中循环的精髓。
还在为 JavaScript 代码写得像“意大利面条”、逻辑混乱难以维护而头秃?收藏本文持续跟进,后续将系统分享 JS 高效语法糖、浏览器兼容与 Polyfill 实战、手写核心源码解析、常见坑点避雷指南,从基础语法到进阶逻辑一站式打通,助你快速提升前端开发硬实力!