文章目录

JavaScript Iterator协议与for-of的自定义可迭代对象

发布于 2026-05-06 18:16:02 · 浏览 10 次 · 评论 0 条

JavaScript Iterator协议与for-of的自定义可迭代对象

JavaScript 原生的 ArrayString 类型可以直接使用 for...of 循环进行遍历。要让自定义的对象也能享受这种语法便利,必须让该对象符合“可迭代协议”。这意味着对象必须包含一个特定的方法,并返回正确的迭代逻辑。


核心概念解析

实现自定义可迭代对象的核心在于理解两个角色之间的关系:

  1. 可迭代对象:对象本身,必须拥有一个 [Symbol.iterator] 属性。
  2. 迭代器对象:由 [Symbol.iterator] 返回的对象,必须拥有一个 next() 方法。

next() 方法每次被调用时,都会返回一个包含两个字面的对象:

  • value:当前遍历到的值。
  • done:布尔值,表示遍历是否结束(true 结束,false 继续)。

步骤 1:构建基础对象结构

首先定义一个简单的类构造函数,用于设定遍历的起始和结束范围。

打开你的代码编辑器或浏览器控制台,输入以下代码:

class NumberRange {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }
}

此时,这个对象仅仅是数据的容器,还不能被 for...of 遍历。如果尝试遍历,JavaScript 会抛出 TypeError: x is not iterable 错误。


步骤 2:实现 [Symbol.iterator] 方法

要让对象变得“可迭代”,需要添加一个特殊的计算属性名 [Symbol.iterator]。这个属性不需要是一个数据属性,而必须是一个函数。

修改上述代码,在类中添加如下方法:

class NumberRange {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  [Symbol.iterator]() {
    // 迭代逻辑将在这里实现
  }
}

步骤 3:编写迭代器逻辑

[Symbol.iterator] 方法内部,必须返回一个包含 next() 方法的对象。这个对象通常可以使用闭包来维护当前的遍历状态(即当前数字是多少)。

完善 [Symbol.iterator] 方法的具体实现:

[Symbol.iterator]() {
  let current = this.start; // 初始化当前指针
  const last = this.end;    // 记录结束边界

  // 返回迭代器对象
  return {
    next() {
      // 1. 判断是否超出边界
      if (current <= last) {
        // 2. 如果未超出,返回当前值,并移动指针
        const value = current;
        current++; // 指针后移
        return { value: value, done: false };
      } else {
        // 3. 如果超出,标记为结束
        return { done: true };
      }
    }
  };
}

这段代码利用闭包保存了 current 变量。每次 next() 被调用时,它都会检查条件并更新状态。


步骤 4:使用 for-of 循环遍历

现在对象已经完全符合协议。实例化一个 NumberRange 对象,并使用 for...of 循环来验证效果。

输入执行以下代码:

const myRange = new NumberRange(1, 5);

for (const num of myRange) {
  console.log(num);
}

控制台将依次输出数字 15,然后自动停止。


深度解析:for-of 的执行流程

理解 for...of 背后发生了什么,有助于掌握更复杂的场景。当循环开始时,JavaScript 引擎会执行一系列固定的操作。

graph TD A[Start for-of loop] --> B[Call obj Symbol.iterator] B --> C[Get Iterator Object] C --> D[Call iterator.next] D --> E{Check result.done} E -- No --> F[Use result.value] F --> D E -- Yes --> G[End Loop]

从流程中可以看出,只要 next() 返回的 donefalse,循环体就会继续执行。一旦 done 变为 true,循环立即终止,且此时返回的对象中的 value 属性通常会被忽略。


进阶应用:实现无限迭代器

迭代协议并不强制要求迭代必须结束。只要控制好 done 的返回时机,就可以创建一个无限序列,例如生成斐波那契数列或无限递增的 ID。

创建一个名为 InfiniteCounter 的类:

class InfiniteCounter {
  constructor(start = 0) {
    this.start = start;
  }

  [Symbol.iterator]() {
    let current = this.start;
    return {
      next() {
        // 永远返回 done: false,实现无限循环
        const value = current;
        current++;
        return { value: value, done: false };
      }
    };
  }
}

注意:由于这个迭代器是无限的,切勿直接使用 for...of 而不带 break 语句,否则会导致浏览器卡死。正确的用法是手动控制迭代次数或使用解构赋值:

const counter = new InfiniteCounter(10);
const iterator = counter[Symbol.iterator]();

// 手动获取前 3 个值
console.log(iterator.next().value); // 10
console.log(iterator.next().value); // 11
console.log(iterator.next().value); // 12

或者使用带 break 的循环:

const counter = new InfiniteCounter(100);
let count = 0;

for (const num of counter) {
  console.log(num);
  count++;
  if (count >= 3) break; // 强制退出
}

常见错误与修复

在实现自定义迭代器时,有几个高频错误点需要注意。

错误现象 可能原因 修正方法
TypeError: obj is not iterable 对象上没有定义 [Symbol.iterator] 方法。 检查对象属性,确保方法名包含方括号。
TypeError: iterator.next is not a function [Symbol.iterator] 没有返回包含 next 方法的对象。 确保 return 语句返回的是字面量对象 { next: function(){} }
循环只执行一次就停止 next() 方法内部没有更新状态变量(如 current 没有自增)。 确认 next 内部修改了闭包变量或对象属性。
输出 undefined donefalse 时,返回对象中缺少 value 字段。 保证返回对象结构完整:{ value: ..., done: ... }

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文