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

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

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

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

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

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

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

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

js 复制代码
for (let i = 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循环就像"只要条件允许就不停干活",适合不知道循环次数,但知道"停止条件"的场景。语法:

js 复制代码
while (循环条件) {
  重复做的事;
}

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

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

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

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

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

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

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

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

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

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

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

避坑点:分号不能漏!

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

三剑客对比表:该选谁?

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

二、循环控制符:break与continue------循环的"刹车"与"跳过"

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

1. break:直接"停掉跑步机"

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

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

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

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

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

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

很多新手会搞混两者:

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

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

js 复制代码
// break版本:遇到3就停
for (let i = 1; i <= 5; i++) {
  if (i === 3) break;
  console.log(i); // 输出1、2
}

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

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

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

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

js 复制代码
标签名: 循环语句 {
  // 循环体
}

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

js 复制代码
// 反面例子:没有label,break只终止内层循环
let 计数 = 0;
for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    if (i === 5 && j === 5) break; // 只终止内层j循环,i循环继续
    计数++;
  }
}
console.log(计数); // 输出95(内层循环到5就停,但i还会继续到9)

// 正面例子:用label,break终止外层循环
let 计数2 = 0;
外层循环: for (let i = 0; i < 10; i++) {
  for (let j = 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的作用是"遍历对象的所有可枚举属性",包括原型链上的属性。语法:

js 复制代码
for (let 属性名 in 对象) {
  操作属性;
}

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

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

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

  • 遍历的是"索引+自定义属性":数组的自定义属性也会被遍历到,而不是只遍历元素。
  • 遍历顺序不固定:可能不是按数组索引顺序遍历。
js 复制代码
// 反面例子:for...in遍历数组
const 水果 = ["苹果", "香蕉", "橙子"];
水果.产地 = "中国"; // 给数组加个自定义属性
for (let i in 水果) {
  console.log(i); // 输出0、1、2、产地(把自定义属性也遍历了!)
}
正确用法:
  • 只用来遍历"普通对象"的属性。

  • 遍历对象时,用hasOwnProperty过滤原型链上的属性(避免遍历到继承的属性):

    js 复制代码
    for (let key in 汽车) {
      if (汽车.hasOwnProperty(key)) { // 只遍历自身属性
        console.log(`${key}:${汽车[key]}`);
      }
    }

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

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

js 复制代码
for (let 值 of 可迭代对象) {
  操作值;
}
核心优势:
  • 遍历数组:直接拿元素值,不关心索引,也不会遍历自定义属性:

    js 复制代码
    const 水果 = ["苹果", "香蕉", "橙子"];

水果.产地 = "中国";

for (let 果 of 水果) {

console.log(果); // 输出苹果、香蕉、橙子(忽略自定义属性)

}

复制代码
- 遍历字符串:直接拿每个字符:
```js
for (let 字符 of "前端开发") {
  console.log(字符); // 输出前、端、开、发
}
  • 遍历Map/Set:直接拿键值对或元素,比for循环简洁太多:

    js 复制代码
    const 学生成绩 = new Map([["小明", 90], ["小红", 85]]);
    for (let [姓名, 分数] of 学生成绩) {
      console.log(`${姓名}:${分数}`); // 输出小明:90 → 小红:85
    }
避坑指南:
  • 不能直接遍历"普通对象":普通对象不是可迭代对象,用for...of遍历会报错。如果要遍历对象,先转成可迭代对象(比如Object.values(对象)):

    js 复制代码
    const 汽车 = { 品牌: "特斯拉", 型号: "Model 3" };
    // 正确:先转成值数组
    for (let 值 of Object.values(汽车)) {
      console.log(值); // 输出特斯拉、Model 3
    }
  • 可以用break/continue控制循环:和基础循环一样支持循环控制符。

for...in vs for...of 对比表

特性 for...in for...of
遍历目标 对象的可枚举属性(含原型链) 可迭代对象的值(数组、Map等)
数组遍历 遍历索引+自定义属性(不推荐) 遍历元素值(推荐)
对象遍历 直接遍历(需过滤原型链) 需转成可迭代对象(如Object.values)
支持的控制符 break/continue break/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跳过当前轮。

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

  • 减少循环内的计算:把循环外能算的东西(比如数组长度)提前缓存,避免每次循环都计算:

    js 复制代码
    // 优化前:每次循环都计算arr.length
    for (let i = 0; i < arr.length; i++) {}
    
    // 优化后:缓存长度
    const len = arr.length;
    for (let i = 0; i < len; i++) {}
  • 避免循环嵌套:嵌套循环的时间复杂度是O(n²),数据量大时会卡顿,能扁平化数据就扁平化。

  • 优先用数组方法:forEach、map、filter等方法(本质也是循环),比手动写for循环更简洁,但要注意forEach不能用break/continue终止(除非抛异常)。

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

相关推荐
爱倒腾的老唐2 小时前
02、打不开某个网站
windows·笔记·电脑
爱上妖精的尾巴2 小时前
6-3 WPS JS宏 add、delete、size、clear集合成员添加与删除
javascript·wps·js宏·jsa
喵了meme2 小时前
Linux学习日记19:线程同步与互斥锁
java·jvm·学习
郑州光合科技余经理2 小时前
海外版生活服务系统源码 | 外卖+跑腿一站式平台技术解析
java·开发语言·javascript·git·spring cloud·php·生活
暗之星瞳2 小时前
python爬虫学习——1
爬虫·python·学习
1024肥宅2 小时前
前端常用模式:提升代码质量的四大核心模式
前端·javascript·设计模式
哆啦A梦15882 小时前
商城后台管理系统 04,商品添加-清空列表
javascript·vue.js·elementui
哆啦A梦15882 小时前
商城后台管理系统 06,类目选择实现
javascript·vue.js·elementui
TL滕3 小时前
从0开始学算法——第十四天(数组与搜索)
数据结构·笔记·学习·算法