for...of 循环与迭代器协议

ES6 (ECMAScript 2015) 引入了许多强大的新特性,其中 for...of 循环和迭代器协议是处理集合数据的重要工具。它们为 JavaScript 提供了一种统一的方式来遍历各种数据结构,从数组到自定义对象。

for...of 循环基础

for...of 是 ES6 新增的循环语法,用于遍历可迭代对象(iterable)的值:

javascript 复制代码
const arr = ['a', 'b', 'c'];

for (const value of arr) {
  console.log(value); // 依次输出 'a', 'b', 'c'
}

与传统的 for 循环和 for...in 循环相比,for...of 提供了更简洁的语法来直接访问集合中的值,而不需要处理索引或键。

可迭代对象与迭代器协议

可迭代对象

一个对象要成为可迭代对象,必须实现 @@iterator 方法(即 Symbol.iterator 属性),该方法返回一个迭代器对象。

JavaScript 中内置的可迭代对象包括:

  • Array
  • String
  • Map
  • Set
  • TypedArray
  • 函数的 arguments 对象
  • DOM 的 NodeList 对象

迭代器协议

迭代器是一个对象,它必须实现 next() 方法,该方法返回一个包含两个属性的对象:

  • value:当前迭代的值
  • done:布尔值,表示迭代是否完成
javascript 复制代码
const arrayIterator = ['a', 'b', 'c'][Symbol.iterator]();

console.log(arrayIterator.next()); // { value: 'a', done: false }
console.log(arrayIterator.next()); // { value: 'b', done: false }
console.log(arrayIterator.next()); // { value: 'c', done: false }
console.log(arrayIterator.next()); // { value: undefined, done: true }

自定义可迭代对象

我们可以通过实现 Symbol.iterator 方法来创建自定义的可迭代对象:

javascript 复制代码
const myIterable = {
  data: [1, 2, 3],
  [Symbol.iterator]() {
    let index = 0;
    return {
      next: () => {
        if (index < this.data.length) {
          return { value: this.data[index++], done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

for (const value of myIterable) {
  console.log(value); // 1, 2, 3
}

for...of 的优势

  1. 简洁性:比传统的 for 循环更简洁
  2. 通用性:可以用于任何实现了迭代器协议的对象
  3. 避免错误:不需要手动管理索引或键
  4. 性能优化:某些情况下比 forEach 更高效

与其他循环的比较

  • for...in:遍历对象的可枚举属性(包括原型链上的),不适合数组遍历
  • forEach:数组方法,不能用于其他可迭代对象,且不能使用 breakcontinue
  • for...of:专门为可迭代对象设计,支持 breakcontinue

实际应用场景

  1. 遍历数组

    javascript 复制代码
    const numbers = [1, 2, 3];
    for (const num of numbers) {
      console.log(num);
    }
  2. 遍历字符串

    javascript 复制代码
    const str = 'hello';
    for (const char of str) {
      console.log(char);
    }
  3. 遍历 Map

    javascript 复制代码
    const map = new Map([['a', 1], ['b', 2]]);
    for (const [key, value] of map) {
      console.log(key, value);
    }
  4. 与解构赋值结合

    javascript 复制代码
    const users = [{name: 'Alice'}, {name: 'Bob'}];
    for (const {name} of users) {
      console.log(name);
    }

注意事项

  1. for...of 不能直接用于普通对象(除非实现了迭代器协议)
  2. 某些旧环境可能需要 polyfill 来支持 for...of
  3. 在迭代过程中修改可迭代对象可能导致意外行为

总结

ES6 的 for...of 循环和迭代器协议为 JavaScript 提供了一种强大而统一的集合遍历机制。通过理解这些概念,开发者可以编写更简洁、更通用的代码来处理各种数据结构。无论是内置集合类型还是自定义对象,只要实现了迭代器协议,都可以使用 for...of 进行遍历,这使得代码更加一致和可维护。