JavaScript 迭代器与生成器

深入解析 JavaScript 迭代协议:迭代器与可迭代对象的定义、生成器函数的执行模型、以及如何使用迭代器惰性处理大数据集。

为什么迭代器是 JavaScript 的核心概念

迭代器(Iterator)和生成器(Generator)是 JavaScript 中处理集合数据的核心概念。它们让我们能够用统一的方式遍历各种数据结构,而且支持惰性求值——按需生成数据,而不是一次性加载所有数据到内存。

for...of 循环、... 展开运算符、Array.from、解构赋值——这些常用功能都依赖迭代器协议。理解迭代器,才能理解 JavaScript 中数据处理的底层机制。


一、迭代器协议

1.1 什么是迭代器

迭代器是一个对象,提供 next() 方法,每次调用返回一个 { done: boolean, value: any } 对象:

const iterator = {
  current = 0,
  next() {
    this.current++;
    if (this.current <= 5) {
      return { done: false, value: this.current };
    }
    return { done: true, value: undefined };
  }
};

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

1.2 可迭代对象

可迭代对象是实现了 Symbol.iterator 方法的对象:

const myArray = [1, 2, 3];
const iterator = myArray[Symbol.iterator]();

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

1.3 内置可迭代对象

JavaScript 中很多对象都是可迭代的:

// 数组
[1, 2, 3][Symbol.iterator]();

// 字符串
"hello"[Symbol.iterator]();

// Map
new Map([['a', 1], ['b', 2]])[Symbol.iterator]();

// Set
new Set([1, 2, 3])[Symbol.iterator]();

// arguments
function args() {
  arguments[Symbol.iterator]();
}

二、生成器函数

2.1 生成器函数定义

生成器函数用 function* 语法定义:

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

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

2.2 yield 的作用

yield 暂停函数执行并返回值;下次 next() 调用时,从暂停点继续:

function* fruitGenerator() {
  console.log('开始');
  yield '苹果';
  console.log('第一个 yield 之后');
  yield '香蕉';
  console.log('第二个 yield 之后');
  yield '橙子';
  console.log('结束');
}

const fruit = fruitGenerator();
console.log(fruit.next());
// 输出: 开始
// { done: false, value: '苹果' }

console.log(fruit.next());
// 输出: 第一个 yield 之后
// { done: false, value: '香蕉' }

2.3 next() 的参数

next(value) 可以向生成器传递值,该值成为 yield 表达式的结果:

function* calculator() {
  const result = yield '开始计算';
  console.log('接收到的值:', result);
  yield result * 2;
}

const calc = calculator();
console.log(calc.next());    // { done: false, value: '开始计算' }
console.log(calc.next(10));   // 传递 10 给 yield,输出: 接收到的值: 10
                              // { done: false, value: 20 }

2.4 throw() 和 return()

function* errorGen() {
  try {
    yield 'a';
    yield 'b';
  } catch (e) {
    console.log('捕获错误:', e);
    yield 'c';
  }
}

const eg = errorGen();
eg.next(); // { value: 'a', done: false }
eg.throw(new Error('error')); // 抛出错误到生成器内部
                              // { value: 'c', done: false }

三、实用迭代器模式

3.1 无限序列

function* fibonacci() {
  let [a, b] = [0, 1];
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

const fib = fibonacci();
fib.next().value; // 0
fib.next().value; // 1
fib.next().value; // 1
fib.next().value; // 2
fib.next().value; // 3

3.2 惰性映射和过滤

function* map(iterable, fn) {
  for (const item of iterable) {
    yield fn(item);
  }
}

function* filter(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) {
      yield item;
    }
  }
}

// 使用
const numbers = [1, 2, 3, 4, 5];
const result = [...map(filter(numbers, n => n % 2 === 0), n => n * 2)];
// [4, 8]

3.3 生成器组合

function* gen1() {
  yield 1;
  yield 2;
}

function* gen2() {
  yield 3;
  yield 4;
}

function* combined() {
  yield* gen1(); // 委托给 gen1
  yield* gen2(); // 委托给 gen2
}

[...combined()]; // [1, 2, 3, 4]

四、迭代器协议与 for...of

4.1 for...of 的工作原理

const arr = ['a', 'b', 'c'];

// for...of 相当于:
const iterator = arr[Symbol.iterator]();
while (true) {
  const { value, done } = iterator.next();
  if (done) break;
  console.log(value);
}

4.2 解构赋值

const [first, ...rest] = 'abc'; // first = 'a', rest = ['b', 'c']

4.3 展开运算符

[...'hello']; // ['h', 'e', 'l', 'l', 'o']

// 数组拼接
[1, 2, ...[3, 4]]; // [1, 2, 3, 4]

// Set 转数组
[...new Set([1, 2, 2, 3])]; // [1, 2, 3]

五、自定义可迭代对象

5.1 实现 Symbol.iterator

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

  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    return {
      next() {
        if (current <= end) {
          return { done: false, value: current++ };
        }
        return { done: true, value: undefined };
      }
    };
  }
}

for (const num of new Range(1, 5)) {
  console.log(num); // 1, 2, 3, 4, 5
}

5.2 异步迭代器

ES2018 引入了异步迭代器:

const asyncIterator = {
  [Symbol.asyncIterator]() {
    return this;
  },
  next() {
    return Promise.resolve({ done: false, value: 'async value' });
  }
};

for await (const value of asyncIterator) {
  console.log(value);
}

六、面试高频考点

考点 1:迭代器协议

迭代器必须实现 next() 方法,返回 { done: boolean, value: any } 对象。可迭代对象实现 Symbol.iterator 方法,返回迭代器。

考点 2:生成器的执行模型

调用生成器函数不执行函数体,而是创建迭代器。每次 next() 调用时,函数执行直到遇到 yield,暂停并返回值。下次 next() 从暂停点继续。

考点 3:for...of 与迭代器

for...of 调用对象的 Symbol.iterator 获取迭代器,然后反复调用 next() 直到 done: true


延展阅读