JavaScript 循环结构:for、for...in 与 for...of 区别
·
JavaScript 循环结构:for、for...in 与 for...of 区别
1. 文档概述
本文档旨在详细说明 JavaScript 中三种常用循环结构(for、for...in、for...of)的语法、核心特点、适用场景及差异,帮助开发者根据业务需求选择最优循环方式,避免使用误区。
| 文档信息 | 说明 |
|---|---|
| 标题 | JS 循环结构区别文档 |
| 适用版本 | ES5+(for...of 为 ES6 新增) |
| 核心目标 | 明确三种循环的适用边界 |
2. 基本 for 循环
for 是 JavaScript 中最基础、灵活性最高的循环结构,通过手动控制索引/条件实现迭代,适用于需精确控制循环过程的场景。
2.1 语法格式
for (初始化表达式; 条件表达式; 增量/减量表达式) {
// 循环体:满足条件时执行的代码
}
- 初始化表达式:循环前执行一次,用于定义索引变量(如
let i = 0)。 - 条件表达式:每次循环前判断,结果为
true时执行循环体(如i < arr.length)。 - 增量/减量表达式:每次循环体执行后执行,用于更新索引(如
i++、i--)。
2.2 核心特点
- 手动管理索引:需显式定义索引的初始值、终止条件及更新逻辑,灵活性高。
- 适用范围广:支持数组、类数组对象(如
arguments)、自定义循环次数的场景。 - 支持循环控制:可通过
break(中断循环)、continue(跳过当前迭代)控制流程。
2.3 使用示例
示例 1:遍历数组(通过索引访问元素)
const fruits = ["apple", "banana", "orange"];
// 初始化索引i=0,条件i<数组长度,每次循环i自增1
for (let i = 0; i < fruits.length; i++) {
console.log(`索引 ${i}:${fruits[i]}`);
// 输出:索引 0:apple → 索引 1:banana → 索引 2:orange
}
示例 2:自定义循环次数(非数组场景)
// 循环输出 1~5 的整数
for (let num = 1; num <= 5; num++) {
console.log(num); // 输出:1 2 3 4 5
}
2.4 注意事项
- 若使用
var定义索引变量,会存在变量提升导致的作用域污染问题,建议用let(块级作用域)。 - 遍历数组时,若数组长度固定,可将长度缓存(
const len = arr.length),避免每次循环重复计算长度。
3. for...in 循环
for...in 专为遍历对象属性设计,迭代对象的可枚举属性(包括自身属性和原型链继承的属性),遍历结果为属性名(而非属性值)。
3.1 语法格式
for (const 属性名变量 in 目标对象) {
// 循环体:处理属性的逻辑
}
3.2 核心特点
- 遍历属性名:迭代对象的所有可枚举属性,变量存储的是属性名(如对象
{name: "Tom"}中,变量会取到"name")。 - 原型链穿透:默认会遍历对象原型链上的可枚举属性,需通过
obj.hasOwnProperty(属性名)过滤自身属性。 - 不适合数组:遍历数组时会将索引视为字符串(如
"0"、"1"),且可能遍历到数组的自定义非数字属性(如arr.custom = "test")。
3.3 使用示例
示例 1:遍历对象(过滤原型链属性)
const user = { name: "Alice", age: 25, gender: "female" };
// 遍历对象属性,用 hasOwnProperty 过滤原型链属性
for (const key in user) {
if (user.hasOwnProperty(key)) { // 关键:确保只处理对象自身属性
console.log(`属性 ${key}:${user[key]}`);
// 输出:属性 name:Alice → 属性 age:25 → 属性 gender:female
}
}
示例 2:不推荐遍历数组(存在隐患)
const arr = [10, 20, 30];
arr.customProp = "不应该被遍历的属性"; // 给数组添加自定义非数字属性
for (const key in arr) {
console.log(key, arr[key]);
// 输出:0 10 → 1 20 → 2 30 → customProp 不应该被遍历的属性(意外包含自定义属性)
}
3.4 注意事项
- 必须过滤原型链:若未使用
hasOwnProperty,可能遍历到原型链上的无关属性(如Object.prototype上的自定义方法)。 - 遍历顺序不确定:对于数字属性(如数组索引),浏览器会按升序遍历,但非数字属性的顺序不固定,不依赖遍历顺序时慎用。
4. for...of 循环(ES6 新增)
for...of 是 ES6 引入的可迭代对象专用循环,直接迭代对象的元素值(而非索引或属性名),适用于数组、字符串、Map、Set 等可迭代对象。
4.1 语法格式
for (const 元素值变量 of 可迭代对象) {
// 循环体:处理元素的逻辑
}
- 可迭代对象:指实现了
Symbol.iterator接口的对象,常见类型包括:数组、字符串、Map、Set、arguments、NodeList(DOM 节点集合)等。
4.2 核心特点
- 直接获取元素值:无需通过索引或属性名间接访问,变量直接存储元素值(如数组
[1,2,3]中,变量取到1、2、3)。 - 不遍历属性:仅关注可迭代对象的“元素”,不会遍历自定义属性(如数组的
customProp不会被迭代)。 - 支持循环控制:可通过
break、continue控制循环,且支持async/await(异步迭代)。 - 不支持普通对象:普通对象(如
{a:1, b:2})默认未实现Symbol.iterator,无法直接用for...of遍历(需手动实现迭代器)。
4.3 使用示例
示例 1:遍历数组
const numbers = [1, 2, 3, 4];
for (const num of numbers) {
if (num % 2 === 0) continue; // 跳过偶数
console.log(num); // 输出:1 → 3
}
示例 2:遍历字符串(按字符迭代)
const str = "hello";
for (const char of str) {
console.log(char); // 输出:h → e → l → l → o
}
示例 3:遍历 Map(按键值对迭代)
const productMap = new Map();
productMap.set("phone", 5999);
productMap.set("laptop", 9999);
// Map 迭代时,元素为 [键, 值] 数组
for (const [key, value] of productMap) {
console.log(`商品 ${key}:${value} 元`);
// 输出:商品 phone:5999 元 → 商品 laptop:9999 元
}
4.4 注意事项
- 普通对象不可直接遍历:若需用
for...of遍历普通对象,需手动实现Symbol.iterator接口,示例如下:
const obj = { a: 1, b: 2 };
// 手动实现迭代器
obj[Symbol.iterator] = function* () {
for (const key of Object.keys(this)) {
yield [key, this[key]];
}
};
// 此时可用 for...of 遍历
for (const [key, value] of obj) {
console.log(key, value); // 输出:a 1 → b 2
}
5. 三种循环的核心差异对比
| 对比维度 | 基本 for 循环 |
for...in 循环 |
for...of 循环 |
|---|---|---|---|
| 适用对象 | 数组、类数组、自定义循环次数 | 普通对象(可枚举属性) | 可迭代对象(数组、字符串、Map 等) |
| 遍历内容 | 需通过索引访问元素 | 对象的属性名(字符串) | 可迭代对象的元素值 |
| 原型链影响 | 无(仅访问自身元素) | 默认遍历原型链属性(需过滤) | 无(仅遍历自身元素) |
| 数组兼容性 | 支持(需手动管理索引) | 不推荐(遍历索引字符串+自定义属性) | 推荐(直接获取元素值) |
| 循环控制 | 支持 break/continue |
支持 break/continue |
支持 break/continue |
| 代码简洁性 | 较繁琐(需写索引逻辑) | 中等(需过滤原型链) | 简洁(直接处理元素) |
6. 最佳实践建议
- 遍历数组:优先使用
for...of(简洁、直接获取元素);若需索引,可结合Array.prototype.entries()使用for...of(如for (const [i, val] of arr.entries()))。 - 遍历普通对象:仅使用
for...in,且必须搭配hasOwnProperty过滤原型链属性;或使用Object.keys(obj)结合for...of(如for (const key of Object.keys(obj)))。 - 自定义循环次数:使用基本
for循环(如倒计时、固定次数迭代)。 - 遍历 Map/Set/字符串:强制使用
for...of(最贴合场景,无需额外处理)。
7. 总结
三种循环结构无“优劣”之分,核心是匹配场景:
for:需精确控制索引或循环次数时用;for...in:仅用于遍历普通对象的可枚举属性(需过滤原型链);for...of:遍历可迭代对象(数组、Map 等)时优先用,兼顾简洁性与可读性。
合理选择循环方式,可提升代码效率与可维护性,避免常见使用误区(如用 for...in 遍历数组)。
更多推荐


所有评论(0)