news 2026/5/11 17:50:52

JavaScript学习笔记:4.循环与迭代

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaScript学习笔记:4.循环与迭代

JavaScript学习笔记:4.循环与迭代

上一篇咱们搞定了JS的“决策术”和“容错术”(控制流与错误处理),这一篇来解锁JS的“高效干活技能”——循环与迭代。如果说条件语句是让JS“会做选择”,那循环就是让JS“会重复做事”:比如批量处理数据、遍历数组、循环请求接口……本质上都是“重复执行一段代码”。

但JS的循环家族成员不少(for、while、do…while、for…in、for…of),各自有擅长的场景,也藏着不少“坑”。今天就用“生活化比喻+实战避坑”的方式,带你吃透这些循环,从此重复工作“一键搞定”,不做无用功~

一、循环三剑客:for、while、do…while——基础重复操作指南

循环三剑客是JS最基础的循环语句,核心作用都是“重复执行代码”,但适用场景和执行逻辑天差地别,就像三种不同的“干活模式”。

1. for循环:有明确步骤的“精准干活”

for循环就像“按流程做事的强迫症”,有明确的“初始化-条件判断-更新步骤”,适合知道循环次数或有明确边界的场景。语法结构:

for(初始化变量;循环条件;更新变量){重复做的事;}

比如“打包5个快递”,步骤清晰:初始化(开始打包第1个)、条件(没到5个就继续)、更新(打包下一个):

// 打包5个快递for(leti=1;i<=5;i++){console.log(`打包第${i}个快递`);}// 输出:打包第1个快递 → 打包第2个 → ... → 打包第5个
核心坑:var vs let的“变量污染陷阱”

这是新手最容易栽的坑!用var声明循环变量会出现“变量提升+函数作用域”的问题,导致循环结束后变量值“串味”:

// 反面例子:用var声明循环变量for(vari=1;i<=3;i++){setTimeout(()=>console.log(i),100);}// 输出:4、4、4(而不是1、2、3)

原因:var声明的i是函数/全局作用域,循环结束后i变成4,setTimeout异步执行时拿到的都是最终的i。

避坑指南:循环变量必须用let声明!let是块级作用域,每次循环都会创建一个独立的i,不会串味:

for(leti=1;i<=3;i++){setTimeout(()=>console.log(i),100);}// 输出:1、2、3(正确)
进阶技巧:省略部分表达式

for循环的三个表达式都可以省略,但分号不能少:

  • 省略初始化:for (; i < 5; i++)(变量在外部声明)
  • 省略条件:for (let i = 1; ; i++)(变成无限循环,需在内部用break终止)
  • 省略更新:for (let i = 1; i < 5; ) { i++; }(更新逻辑写在循环体内)

2. while循环:条件满足就“一直干”

while循环就像“只要条件允许就不停干活”,适合不知道循环次数,但知道“停止条件”的场景。语法:

while(循环条件){重复做的事;}

比如“只要奶茶没喝完,就一直吸”:

let奶茶剩余量=50;// 单位:mlwhile(奶茶剩余量>0){奶茶剩余量-=10;console.log(`吸了10ml,还剩${奶茶剩余量}ml`);}// 输出:吸了10ml,还剩40ml → ... → 吸了10ml,还剩0ml
致命坑:无限循环!

while循环的条件如果永远为true,就会陷入无限循环,直接让浏览器卡死(比如忘记更新循环变量):

// 反面例子:无限循环(永远true)while(true){console.log("一直输出,停不下来!");}

避坑指南:确保循环体内有“让条件变false”的逻辑(比如更新变量、break语句),永远不要写无终止条件的while(true)(除非故意用break控制)。

3. do…while循环:先干一次,再看条件

do…while循环是“冲动型干活”:不管条件满足与否,先执行一次循环体,再判断是否继续。语法:

do{重复做的事;}while(循环条件);

比如“先喝一口奶茶,再看要不要续杯”:

let想续杯=false;do{console.log("先喝一口奶茶");}while(想续杯);// 输出:先喝一口奶茶(即使想续杯是false,也执行了一次)
适用场景:必须执行一次的操作

比如用户登录时“先验证一次表单,再判断是否重新输入”、初始化数据时“先加载一次,再判断是否需要更新”。

避坑点:分号不能漏!

do…while的结尾必须加分号(while (条件);),否则会报错——这是唯一需要结尾加分号的循环语句。

三剑客对比表:该选谁?

循环类型执行逻辑适用场景核心注意点
for初始化→条件→执行→更新知道循环次数/有明确边界用let声明变量,避免污染
while条件→执行→更新不知道次数,但知道停止条件防止无限循环
do…while执行→条件→更新必须执行至少一次结尾加分号

二、循环控制符:break与continue——循环的“刹车”与“跳过”

如果说循环是“自动跑步机”,那break和continue就是“刹车”和“跳过当前坡度”——用来控制循环的执行流程,避免无效执行。

1. break:直接“停掉跑步机”

break的作用是“立即终止当前循环/switch”,不管后续条件是否满足。比如“找数组里的目标值,找到就停”:

const水果数组=["苹果","香蕉","橙子","葡萄"];let目标水果="橙子";for(leti=0;i<水果数组.length;i++){if(水果数组[i]===目标水果){console.log(`找到${目标水果},索引是${i}`);break;// 找到就终止循环,不用再找了}}// 输出:找到橙子,索引是2(循环只执行3次,不是4次)

2. continue:“跳过当前步,继续下一轮”

continue的作用是“跳过循环体剩余代码,直接进入下一轮循环”,不会终止整个循环。比如“筛选数组,只打印偶数”:

for(leti=1;i<=5;i++){if(i%2!==0){continue;// 不是偶数,跳过后面的打印}console.log(`偶数:${i}`);}// 输出:偶数:2 → 偶数:4

3. 易错点:break vs continue的区别

很多新手会搞混两者:

  • break:“我不干了,整个循环都停”
  • continue:“这一轮不干了,下一轮再来”

举个例子,同样是“遇到3就操作”:

// break版本:遇到3就停for(leti=1;i<=5;i++){if(i===3)break;console.log(i);// 输出1、2}// continue版本:遇到3跳过,继续下一轮for(leti=1;i<=5;i++){if(i===3)continue;console.log(i);// 输出1、2、4、5}

三、label语句:多层循环的“精准导航”

当遇到“循环嵌套”(比如双层for循环)时,break和continue默认只作用于“当前循环”,这时候label语句就能派上用场——给循环贴个“标签”,让break/continue精准控制外层循环。

label就像“给循环起个名字”,语法:

标签名:循环语句{// 循环体}

比如“双层循环找坐标(5,5),找到就终止所有循环”:

// 反面例子:没有label,break只终止内层循环let计数=0;for(leti=0;i<10;i++){for(letj=0;j<10;j++){if(i===5&&j===5)break;// 只终止内层j循环,i循环继续计数++;}}console.log(计数);// 输出95(内层循环到5就停,但i还会继续到9)// 正面例子:用label,break终止外层循环let计数2=0;外层循环:for(leti=0;i<10;i++){for(letj=0;j<10;j++){if(i===5&&j===5)break外层循环;// 直接终止外层循环计数2++;}}console.log(计数2);// 输出55(找到(5,5)就停,总共执行55次)
避坑指南:
  • label只能标识“循环语句”或“块语句”,不能标识单独的语句。
  • 不要滥用label:多层循环很少见,用label会让代码可读性变差,能拆分成函数就尽量拆分。

四、迭代神器:for…in与for…of——遍历对象/数组的“专属工具”

如果说基础循环是“通用工具”,那for…in和for…of就是“专用工具”——专门用来遍历对象或可迭代对象(数组、Map、Set等),比基础循环更简洁。

1. for…in:遍历对象的“属性探测器”

for…in的作用是“遍历对象的所有可枚举属性”,包括原型链上的属性。语法:

for(let属性名in对象){操作属性;}

比如遍历汽车对象的属性:

const汽车={品牌:"特斯拉",型号:"Model 3",价格:23.99};for(letkeyin汽车){console.log(`${key}${汽车[key]}`);}// 输出:品牌:特斯拉 → 型号:Model 3 → 价格:23.99
致命坑:千万别用for…in遍历数组!

很多新手会犯这个错,但for…in遍历数组有两个致命问题:

  • 遍历的是“索引+自定义属性”:数组的自定义属性也会被遍历到,而不是只遍历元素。
  • 遍历顺序不固定:可能不是按数组索引顺序遍历。
// 反面例子:for...in遍历数组const水果=["苹果","香蕉","橙子"];水果.产地="中国";// 给数组加个自定义属性for(letiin水果){console.log(i);// 输出0、1、2、产地(把自定义属性也遍历了!)}
正确用法:
  • 只用来遍历“普通对象”的属性。
  • 遍历对象时,用hasOwnProperty过滤原型链上的属性(避免遍历到继承的属性):
    for(letkeyin汽车){if(汽车.hasOwnProperty(key)){// 只遍历自身属性console.log(`${key}${汽车[key]}`);}}

2. for…of:遍历可迭代对象的“值提取器”

for…of是ES6新增的迭代语句,专门用来遍历“可迭代对象”(数组、Map、Set、字符串、arguments等),直接遍历“值”而不是索引或属性,比for…in更安全、更简洁。语法:

for(letof可迭代对象){操作值;}
核心优势:
  • 遍历数组:直接拿元素值,不关心索引,也不会遍历自定义属性:
    const水果=["苹果","香蕉","橙子"];

水果.产地 = “中国”;
for (let 果 of 水果) {
console.log(果); // 输出苹果、香蕉、橙子(忽略自定义属性)
}

- 遍历字符串:直接拿每个字符: ```js for (let 字符 of "前端开发") { console.log(字符); // 输出前、端、开、发 }
  • 遍历Map/Set:直接拿键值对或元素,比for循环简洁太多:
    const学生成绩=newMap([["小明",90],["小红",85]]);for(let[姓名,分数]of学生成绩){console.log(`${姓名}${分数}`);// 输出小明:90 → 小红:85}
避坑指南:
  • 不能直接遍历“普通对象”:普通对象不是可迭代对象,用for…of遍历会报错。如果要遍历对象,先转成可迭代对象(比如Object.values(对象)):
    const汽车={品牌:"特斯拉",型号:"Model 3"};// 正确:先转成值数组for(letofObject.values(汽车)){console.log();// 输出特斯拉、Model 3}
  • 可以用break/continue控制循环:和基础循环一样支持循环控制符。

for…in vs for…of 对比表

特性for…infor…of
遍历目标对象的可枚举属性(含原型链)可迭代对象的值(数组、Map等)
数组遍历遍历索引+自定义属性(不推荐)遍历元素值(推荐)
对象遍历直接遍历(需过滤原型链)需转成可迭代对象(如Object.values)
支持的控制符break/continuebreak/continue

五、循环实战避坑总结

  1. 基础循环选对场景:知道次数用for,不知道次数用while,必须执行一次用do…while。
  2. 循环变量用let:避免var的变量污染和异步问题。
  3. 防止无限循环:while循环必须有终止条件,for循环不能省略更新表达式。
  4. 遍历数组用for…of:别用for…in,否则会遍历自定义属性。
  5. 遍历对象用for…in+hasOwnProperty:或Object.values+for…of。
  6. 多层循环少用label:尽量拆分成函数,提高可读性。
  7. break和continue别搞混:break终止循环,continue跳过当前轮。

六、最后:循环的“效率秘籍”

  • 减少循环内的计算:把循环外能算的东西(比如数组长度)提前缓存,避免每次循环都计算:
    // 优化前:每次循环都计算arr.lengthfor(leti=0;i<arr.length;i++){}// 优化后:缓存长度constlen=arr.length;for(leti=0;i<len;i++){}
  • 避免循环嵌套:嵌套循环的时间复杂度是O(n²),数据量大时会卡顿,能扁平化数据就扁平化。
  • 优先用数组方法:forEach、map、filter等方法(本质也是循环),比手动写for循环更简洁,但要注意forEach不能用break/continue终止(除非抛异常)。

循环与迭代是JS处理重复任务的核心,选对循环类型、避开常见坑,能让你的代码既简洁又高效。下一篇笔记,我们会聊JS的函数——这是JS的“代码复用神器”,让你写出可复用、高内聚的代码。关注我,继续解锁JS的实战技能,从“会写”到“写好”,一步步成为JS高手~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 10:24:01

EtherCAT 逐帧解析状态机切换过程(初始清零阶段)

第一阶段总结&#xff1a;主站通过一系列广播写&#xff08;BWR&#xff09; 和广播读&#xff08;BRD&#xff09; 操作&#xff0c;完成从站通信基础配置与状态校准。下面逐帧解析初始化报文&#xff1a; 1、BWR 报文向地址101&#xff08;对应 DL control 寄存器&#xff09…

作者头像 李华
网站建设 2026/5/8 12:58:09

如何快速搭建专业的学术个人主页:AcadHomepage完整指南

如何快速搭建专业的学术个人主页&#xff1a;AcadHomepage完整指南 【免费下载链接】acad-homepage.github.io AcadHomepage: A Modern and Responsive Academic Personal Homepage 项目地址: https://gitcode.com/gh_mirrors/ac/acad-homepage.github.io 作为一名学者或…

作者头像 李华
网站建设 2026/5/3 12:26:40

高频IO服务优化实战指南

文章目录高频IO服务优化实战指南Netty、Reactor模型JVM消耗、零拷贝与IO线程调度深度解析&#x1f4cb; 目录⚡ 一、高频IO服务的挑战与特征&#x1f4a1; 高频IO服务特征分析&#x1f3af; 高频IO性能指标&#x1f527; 二、Netty与Reactor模型的JVM消耗分析&#x1f4a1; Net…

作者头像 李华
网站建设 2026/5/10 23:04:52

IEC61131-3工业自动化编程:5大核心语言全面解析与实战指南

IEC61131-3工业自动化编程&#xff1a;5大核心语言全面解析与实战指南 【免费下载链接】IEC61131-3编程语言及应用基础 IEC61131-3编程语言及应用基础 项目地址: https://gitcode.com/Open-source-documentation-tutorial/44794 在工业自动化快速发展的今天&#xff0c;…

作者头像 李华
网站建设 2026/5/9 6:06:11

Graphic:Flutter数据可视化的终极指南 - 从语法理论到图表实战

Graphic&#xff1a;Flutter数据可视化的终极指南 - 从语法理论到图表实战 【免费下载链接】graphic A grammar of data visualization and Flutter charting library. 项目地址: https://gitcode.com/gh_mirrors/gr/graphic Graphic是一个基于Flutter的开源数据可视化库…

作者头像 李华
网站建设 2026/5/8 15:27:07

施耐德BMEH586040S:高效节能型高性能过程变频驱动平台

产品定位 施耐德电气BMEH586040S是Altivar Process系列中的一款高效节能型高性能模块化变频器&#xff0c;专为对能效、可靠性与系统集成有严苛要求的连续过程工业及关键基础设施设计。该平台集卓越的电机控制、尖端的能源管理功能与开放的数字化连接于一体&#xff0c;是实现核…

作者头像 李华