为什么迭代器是 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。