迭代器与生成器全面理解

在 JavaScript 中,迭代(Iteration) 是一种逐个访问数据集合中元素的机制。ES6 引入了 迭代协议(Iteration Protocols) ,使得数组、字符串、MapSet 等对象可以被统一访问。与此同时,生成器(Generator) 提供了更灵活的迭代方式。本文将系统介绍这两个核心概念。


一、迭代器 (Iterator)

1. 理解迭代

迭代就是 按顺序逐个访问集合中的元素。常见场景:

ini 复制代码
const arr = [10, 20, 30];
for (let item of arr) {
  console.log(item);
}
// 输出:10, 20, 30

for...of 能运行的原因,就是数组实现了 可迭代协议


2. 迭代器模式

迭代器模式(Iterator Pattern)是一种设计模式,用于提供一个统一的接口来顺序访问集合对象,而不暴露集合的内部结构。

核心思想:

  • 提供一个"迭代器对象"
  • 通过 .next() 方法顺序获取下一个值
  • 每次返回一个 { value, done } 对象

示例:

vbnet 复制代码
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

3. 可迭代协议(Iterable Protocol)

一个对象如果实现了 Symbol.iterator 方法,并且该方法返回一个迭代器对象,那么它就是 可迭代对象

常见的可迭代对象:

  • Array
  • String
  • Map
  • Set
  • TypedArray
  • arguments 对象
  • DOM 集合(如 NodeList

示例:

rust 复制代码
const str = "Hi";
for (let ch of str) {
  console.log(ch); // H i
}

4. 迭代器协议(Iterator Protocol)

一个对象如果实现了 .next() 方法,且返回 { value, done } 对象,就符合迭代器协议。

示例:

javascript 复制代码
const myIterator = {
  current: 0,
  next() {
    this.current++;
    if (this.current <= 3) {
      return { value: this.current, done: false };
    } else {
      return { value: undefined, done: true };
    }
  }
};

console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }

5. 自定义迭代器协议

我们可以通过实现 Symbol.iterator 来让自定义对象可迭代。

示例:

javascript 复制代码
const range = {
  start: 1,
  end: 5,
  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    return {
      next() {
        if (current <= end) {
          return { value: current++, done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
};

for (let num of range) {
  console.log(num); // 1, 2, 3, 4, 5
}

6. 提前终止迭代器协议

迭代器还可以定义 return() 方法,在迭代提前中止时调用。

示例:

javascript 复制代码
const obj = {
  [Symbol.iterator]() {
    let i = 0;
    return {
      next() {
        return { value: i++, done: i > 5 };
      },
      return() {
        console.log("迭代器被提前终止");
        return { done: true };
      }
    };
  }
};

for (let n of obj) {
  if (n === 2) break;
  console.log(n);
}
// 输出:
// 0
// 1
// 迭代器被提前终止

二、生成器 (Generator)

1. 生成器基础

生成器是一个特殊的函数,使用 function* 定义。它能生成一个迭代器对象。

lua 复制代码
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

const it = gen();
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }

2. 通过 yield 中断执行

yield 会中断函数执行,并将值返回给调用方;再次调用 .next() 时,函数会从上次中断处继续。

lua 复制代码
function* counter() {
  console.log("start");
  yield 1;
  console.log("middle");
  yield 2;
  console.log("end");
}

const g = counter();
g.next(); // 输出 "start",返回 { value: 1, done: false }
g.next(); // 输出 "middle",返回 { value: 2, done: false }
g.next(); // 输出 "end",返回 { value: undefined, done: true }

3. 生成器作为默认迭代器

生成器天然实现了 可迭代协议 ,因此可以直接用在 for...of 或解构中。

javascript 复制代码
function* numbers() {
  yield 10;
  yield 20;
  yield 30;
}

for (let n of numbers()) {
  console.log(n);
}
// 输出:10, 20, 30

4. 提前终止生成器

生成器对象同样支持 .return() 方法来终止。

lua 复制代码
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

const g = gen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.return("结束")); // { value: "结束", done: true }
console.log(g.next()); // { value: undefined, done: true }

总结

  • 迭代协议 提供了统一的遍历机制。
  • 可迭代对象 实现 Symbol.iterator
  • 迭代器对象 实现 next(),可选 return()
  • 生成器 是实现迭代协议的语法糖,使用 yield 来中断/恢复执行。
  • 提前终止 可通过 return() 实现。

迭代器和生成器不仅让代码更简洁,还为 异步编程、数据流控制 等高级应用打下了基础。

相关推荐
AliciaIr12 分钟前
深入理解HTTP:从协议基础到版本演进(上)
前端·http
pepedd86415 分钟前
数组字符串方法有哪些-带你重温js基础
前端·javascript·trae
pepedd86416 分钟前
深入理解js作用域-你真的懂js吗
前端·javascript·trae
阿迪州17 分钟前
[函数式编程] 为什么要柯里化?
前端
Cache技术分享26 分钟前
162. Java Lambda 表达式 - Consumer 的链式组合
前端·后端
是晓晓吖30 分钟前
为什么在Tab中取不到content.js给window设置的变量/函数?
前端·chrome
日月晨曦32 分钟前
JS闭包:变量的"守护者"与"储物间"
前端·javascript
袁煦丞33 分钟前
轻量级网络大佬Nginx打开公网自由之路:cpolar内网穿透实验室第625个成功挑战
前端·程序员·远程工作
日月晨曦34 分钟前
TypeScript:让JavaScript穿上西装革履
前端·typescript
cvpv34 分钟前
优雅!太优雅!斯巴拉西!怎么让AI写出最优雅的代码
前端·typescript·trae