ES6 迭代避坑:for...of 与 for...in 终极对比

for...of 是 ES6 引入的迭代循环语法,专门用于遍历可迭代对象(Iterable),语法简洁、语义清晰,避免了传统 for 循环的索引冗余和 for...in 的原型链污染问题。

一、核心概念:可迭代对象

for...of 只能遍历可迭代对象,即部署了 [Symbol.iterator] 方法的对象。JavaScript 中默认支持的可迭代对象包括:

  • 数组(Array)
  • 字符串(String)
  • 集合(Set)
  • 映射(Map)
  • 类数组对象(如 arguments、DOM 集合 NodeList
  • Generator 对象

二、基本语法

for (const 变量 of 可迭代对象) {
  // 循环体:变量每次迭代对应对象的一个元素
}
  • 变量:接收每次迭代的元素值(而非索引,与 for...in 不同)。
  • 建议用 const(元素不可修改),若需修改元素(如数组元素),可用 let

三、常见使用场景

1. 遍历数组(最常用)

直接获取数组元素,无需索引:

const arr = ['a', 'b', 'c'];
for (const item of arr) {
  console.log(item); // 输出:a、b、c
}

// 需修改元素时用 let
const nums = [1, 2, 3];
for (let num of nums) {
  num *= 2; // 修改循环变量(不影响原数组,因是值类型)
  console.log(num); // 2、4、6
}
console.log(nums); // [1,2,3](原数组未变)

// 若需修改原数组,需结合索引(可用 Array.prototype.entries())
for (const [index, num] of nums.entries()) {
  nums[index] = num * 2; // 通过索引修改原数组
}
console.log(nums); // [2,4,6]
2. 遍历字符串

字符串按字符迭代(含 Unicode 字符,如中文、表情):

const str = 'hello世界😀';
for (const char of str) {
  console.log(char); // 输出:h、e、l、l、o、世、界、😀
}
3. 遍历 Set

Set 无重复元素,迭代时直接获取元素值:

const set = new Set([1, 2, 2, 3]);
for (const val of set) {
  console.log(val); // 输出:1、2、3(自动去重)
}
4. 遍历 Map

Map 迭代时返回 [key, value] 数组,需解构获取键值:

const map = new Map([
  ['name', '张三'],
  ['age', 20]
]);
for (const [key, value] of map) {
  console.log(`${key}: ${value}`); // 输出:name: 张三、age: 20
}

// 单独遍历键/值
for (const key of map.keys()) { /* ... */ }
for (const value of map.values()) { /* ... */ }
5. 遍历类数组对象

如 NodeList(DOM 元素集合)、arguments

// 遍历 DOM 元素(NodeList)
const lis = document.querySelectorAll('li');
for (const li of lis) {
  li.style.color = 'red'; // 批量修改样式
}

// 遍历 arguments(函数参数集合)
function sum() {
  let total = 0;
  for (const num of arguments) {
    total += num;
  }
  return total;
}
console.log(sum(1,2,3)); // 6
6. 遍历 Generator 对象

Generator 是专门的迭代器生成函数,配合 for...of 自动迭代:

function* generator() {
  yield 1;
  yield 2;
  yield 3;
}

for (const val of generator()) {
  console.log(val); // 输出:1、2、3
}

四、for...of 与 for...in 的区别(关键!)

特性for...offor...in
遍历目标元素值(可迭代对象)索引 / 属性名(对象)
适用对象数组、字符串、Set、Map 等可迭代对象任意对象(含数组、普通对象)
原型链属性不遍历(仅自身可迭代元素)会遍历原型链上的可枚举属性
数组空位(empty)跳过空位(不处理)视为 undefined(遍历空位索引)

反例:for...in 遍历数组的问题

const arr = [1, 2, 3];
// 给数组原型添加属性(污染)
Array.prototype.test = '原型属性';

for (const key in arr) {
  console.log(key); // 输出:0、1、2、test(遍历了原型属性)
}

for (const val of arr) {
  console.log(val); // 输出:1、2、3(忽略原型属性)
}

五、中断 for...of 循环

支持 breakcontinuereturn(函数内)中断循环,与普通 for 循环一致:

const arr = [1, 2, 3, 4, 5];
for (const val of arr) {
  if (val === 3) break; // 遇到 3 终止循环
  console.log(val); // 输出:1、2
}

for (const val of arr) {
  if (val % 2 === 0) continue; // 跳过偶数
  console.log(val); // 输出:1、3、5
}

六、自定义可迭代对象(进阶)

若想让普通对象支持 for...of,需手动部署 [Symbol.iterator] 方法:

const obj = {
  name: '张三',
  age: 20,
  hobbies: ['篮球', '游戏'],
  // 部署迭代器方法
  [Symbol.iterator]() {
    let index = 0;
    const hobbies = this.hobbies;
    // 返回迭代器对象(含 next() 方法)
    return {
      next() {
        if (index < hobbies.length) {
          return { value: hobbies[index++], done: false }; // 未结束
        } else {
          return { value: undefined, done: true }; // 已结束
        }
      }
    };
  }
};

// 现在 obj 可被 for...of 遍历(遍历 hobbies 数组)
for (const hobby of obj) {
  console.log(hobby); // 输出:篮球、游戏
}

总结

for...of 是遍历可迭代对象的最优选择

  • 语法简洁,直接获取元素值,无需处理索引;
  • 避免 for...in 的原型链污染问题;
  • 支持所有常见数据结构(数组、字符串、Set、Map 等);
  • 可灵活中断循环。

日常开发中,优先用 for...of 遍历数组、字符串、集合等,仅在需要遍历普通对象的属性时考虑 for...in(或 Object.keys())。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

canjun_wen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值