JavaScript中的循环特点和区别

一、前言:循环在 JS 中的核心地位

循环是编程的基础逻辑之一,在 JavaScript 中更是贯穿前端开发、Node.js 后端等所有场景 ------ 从 DOM 遍历、数据处理到异步流程控制,几乎无处不在。但多数开发者仅停留在for/forEach的基础使用,忽略了不同循环的适用场景、性能差异和进阶技巧。本文将从基础语法到实战优化,全面拆解 JS 循环的核心知识点,帮你掌握 "什么时候用什么循环" 的底层逻辑。

二、JS 循环的 5 大核心类型:语法与基础用法

1. 传统循环:for/while/do...while

这是最基础的循环类型,兼容性覆盖所有浏览器(包括 IE),核心优势是完全可控(可中断、可跳过、可反向遍历)。

  • for 循环:适合已知遍历次数的场景
复制代码

// 正向遍历数组

const arr = [1, 2, 3, 4];

for (let i = 0; i < arr.length; i++) {

console.log(arr[i]); // 1,2,3,4

}

// 反向遍历(性能更优,减少数组长度读取)

for (let i = arr.length - 1; i >= 0; i--) {

console.log(arr[i]); // 4,3,2,1

}

  • while 循环:适合未知遍历次数的场景(需手动控制终止条件)
复制代码

let count = 0;

while (count ) {

console.log(count); // 0,1,2

count++;

}

  • do...while 循环 :与while的区别是至少执行一次
复制代码

let count = 3;

do {

console.log(count); // 3(即使条件不满足,仍执行一次)

count++;

} while (count );

2. 数组专用循环:forEach

ES5 引入的数组遍历方法,语法简洁,无需手动控制索引,但无法中断循环(break/return无效,return仅相当于跳过当前迭代)。

复制代码

const arr = [1, 2, 3];

arr.forEach((item, index, array) => {

if (item === 2) return; // 跳过当前项,继续执行下一次

console.log(item); // 1,3

});

⚠️ 注意:forEach遍历稀疏数组时,会跳过空元素(与for循环不同)。

3. 迭代器循环:for...in/for...of

ES6 引入的新型循环,基于迭代器(Iterator)协议,适用于不同数据结构的遍历。

  • for...in :遍历对象的可枚举属性(包括原型链上的属性),不建议用于数组(会遍历数组的索引及自定义属性)。
复制代码

const obj = { name: "张三", age: 20 };

for (const key in obj) {

// 过滤原型链属性

if (obj.hasOwnProperty(key)) {

console.log(`${key}: ${obj[key]}`); // name: 张三, age: 20

}

}

  • for...of :遍历可迭代对象 (数组、字符串、Map、Set 等)的,支持break/continue/return中断循环,是数组遍历的优选方案。
复制代码

// 遍历数组

const arr = [1, 2, 3];

for (const item of arr) {

if (item === 2) break; // 中断循环

console.log(item); // 1

}

// 遍历Map

const map = new Map([["name", "张三"], ["age", 20]]);

for (const [key, value] of map) {

console.log(`${key}: ${value}`); // name: 张三, age: 20

}

三、关键对比:不同循环的适用场景与性能

1. 功能对比表

|------------------|------------|------|-------|--------|
| 循环类型 | 适用场景 | 能否中断 | 遍历空元素 | 遍历原型属性 |
| for | 数组、已知次数的遍历 | 是 | 是 | - |
| while/do...while | 未知次数的遍历 | 是 | - | - |
| forEach | 数组遍历(无需中断) | 否 | 否 | - |
| for...in | 对象属性遍历 | 是 | - | 是(需过滤) |
| for...of | 可迭代对象的值遍历 | 是 | 否(数组) | - |

2. 性能分析(基于 Chrome 浏览器测试)
  • 性能排序(从快到慢):for(反向)> for(正向)> for...of > forEach > for...in
  • 核心原因:
    • for循环无额外函数调用和迭代器开销,直接操作索引,性能最优;
    • for...of基于迭代器,但底层优化较好,性能接近for;
    • forEach每次迭代需调用回调函数,存在函数调用开销;
    • for...in需遍历原型链属性,且需判断属性是否为自身所有,性能最差。

⚠️ 注意:性能差异仅在大数据量(10 万 + 条数据) 下明显,日常开发中优先考虑代码可读性,无需过度优化。

四、进阶技巧:循环的高级用法与避坑指南

1. 中断循环的正确方式
  • 可中断循环:for/while/do...while/for...of(用break中断,continue跳过当前迭代);
  • 不可中断循环:forEach(需中断时,建议替换为for...of或for循环);
  • 替代方案:数组方法some/every(本质是遍历,但可通过return true中断):
复制代码

const arr = [1, 2, 3];

// some:找到满足条件的项后中断,返回true

arr.some(item => {

if (item === 2) return true;

console.log(item); // 1

});

2. 异步循环的坑与解决方案

问题:forEach/for...in中使用异步操作(如setTimeout、接口请求)时,循环会先执行完,异步回调才触发,导致索引 / 值错乱。

复制代码

// 错误示例:所有回调都打印4(i最终为4)

const arr = [1, 2, 3];

for (var i = 0; i < arr.length; i++) {

setTimeout(() => console.log(arr[i]), 0);

}

解决方案

  • 用let声明索引(块级作用域,每次迭代创建独立变量);
  • 用for...of(天然支持异步中断);
  • 用Promise.all(并行执行异步操作)。
复制代码

// 正确示例:用for...of

const arr = [1, 2, 3];

async function asyncLoop() {

for (const item of arr) {

await new Promise(resolve => setTimeout(() => {

console.log(item); // 1,2,3(顺序执行)

resolve();

}, 1000));

}

}

asyncLoop();

3. 循环优化技巧
  • 数组遍历前缓存长度:`for (let i = 0, len = arr.length; i (避免每次迭代读取数组长度);
  • 反向遍历数组:减少数组索引边界判断(i >= 0比 `i 简单);
  • 大数据量遍历用for或for...of,避免forEach和for...in;
  • 遍历对象属性时,用Object.keys(obj).forEach替代for...in(避免原型链问题)。

五、实战场景:循环的最佳实践

  1. 场景 1:数组遍历并中断 → 用for...of或for循环;
  1. 场景 2:数组遍历无需中断,追求简洁 → 用forEach;
  1. 场景 3:对象属性遍历 → 用Object.keys(obj).forEach或for...in(需过滤原型链);
  1. 场景 4:Map/Set 遍历 → 用for...of;
  1. 场景 5:异步遍历(顺序执行) → 用for...of+async/await;
  1. 场景 6:异步遍历(并行执行) → 用Promise.all(arr.map(async item => {}))。

六、总结

JavaScript 循环的核心是 "选择合适的工具解决对应问题":基础场景用for/forEach,对象遍历用Object.keys+forEach,可迭代对象用for...of,异步场景用for...of+async/await。掌握不同循环的语法、性能和适用场景,不仅能提升代码可读性,还能在大数据量场景下优化性能。

记住:没有最好的循环,只有最适合的循环。在实际开发中,需结合业务场景、代码可读性和性能需求综合选择。

相关推荐
我命由我123452 小时前
Python 开发 - OpenAI 兼容阿里云百炼平台 API
开发语言·人工智能·后端·python·阿里云·ai·语言模型
GokuCode2 小时前
【GO高级编程】02.GO接收者概述
开发语言·后端·golang
Aotman_2 小时前
JavaScript去除对象字段空格
开发语言·前端·javascript
云和数据.ChenGuang2 小时前
Zabbix 6 与 PHP 5 版本**完全不兼容
运维·开发语言·php·zabbix·运维工程师
csbysj20202 小时前
Ruby 范围(Range)
开发语言
苏 凉2 小时前
在 openEuler 24.03 LTS SP2 上安装部署 iSula 容器引擎及性能测试
开发语言·rust
爱网安的monkey brother2 小时前
vue3+ts项目自建训练
前端·javascript·vue.js
qq_336313932 小时前
HashMap
java·开发语言