29. 说说你对Iterator和Generator的理解?
问题解析
Iterator(迭代器)和 Generator(生成器)是 ES6 引入的重要特性,它们提供了一种更优雅的方式来处理数据集合和异步编程。面试中主要考察它们的基本概念、实现方式、使用场景以及相互关系。
核心概念
1. Iterator(迭代器)
Iterator 是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署了 Iterator 接口,就可以完成遍历操作。
// 迭代器的基本结构
const iterator = {
next() {
return {
value: /* 当前值 */,
done: /* 是否遍历完成 */
};
}
};
// 示例
function createIterator(array) {
let index = 0;
return {
next() {
if (index < array.length) {
return { value: array[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
const iter = createIterator([1, 2, 3]);
console.log(iter.next()); // { value: 1, done: false }
console.log(iter.next()); // { value: 2, done: false }
console.log(iter.next()); // { value: 3, done: false }
console.log(iter.next()); // { value: undefined, done: true }
2. 可迭代协议(Iterable Protocol)
一个对象要成为可迭代对象,必须实现 @@iterator 方法(即 Symbol.iterator)。
// 使对象可迭代
const iterable = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
const data = this.data;
return {
next() {
if (index < data.length) {
return { value: data[index++], done: false };
}
return { done: true };
}
};
}
};
// 使用 for...of 遍历
for (const item of iterable) {
console.log(item); // 1, 2, 3
}
// 使用展开运算符
console.log([...iterable]); // [1, 2, 3]
3. Generator(生成器)
Generator 是一种特殊的函数,可以暂停执行和恢复执行,使用 function* 语法定义。
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
const gen = generatorFunction();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
详细解答
1. 原生可迭代对象
// JavaScript 内置的可迭代对象
// 1. Array
const arr = [1, 2, 3];
for (const item of arr) console.log(item);
// 2. String
const str = 'hello';
for (const char of str) console.log(char);
// 3. Map
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) console.log(key, value);
// 4. Set
const set = new Set([1, 2, 3]);
for (const item of set) console.log(item);
// 5. TypedArray
const typedArray = new Uint8Array([1, 2, 3]);
for (const item of typedArray) console.log(item);
// 6. arguments 对象
function test() {
for (const arg of arguments) console.log(arg);
}
test(1, 2, 3);
// 7. NodeList
// const nodes = document.querySelectorAll('div');
// for (const node of nodes) console.log(node);
2. Generator 的详细用法
// 1. 基本用法
function* simpleGenerator() {
yield 'first';
yield 'second';
return 'done';
}
const gen = simpleGenerator();
console.log(gen.next()); // { value: 'first', done: false }
console.log(gen.next()); // { value: 'second', done: false }
console.log(gen.next()); // { value: 'done', done: true }
console.log(gen.next()); // { value: undefined, done: true }
// 2. yield 表达式
function* yieldExpression() {
const received = yield 'sent'; // yield 可以接收值
console.log('Received:', received);
yield 'next';
}
const gen2 = yieldExpression();
console.log(gen2.next()); // { value: 'sent', done: false }
console.log(gen2.next('value')); // 传入值给上一个 yield
// 输出: Received: value
// 返回: { value: 'next', done: false }
// 3. 在 Generator 中使用 return
function* withReturn() {
yield 1;
yield 2;
return 3; // return 会设置 done 为 true
yield 4; // 不会执行
}
const gen3 = withReturn();
console.log([...gen3]); // [1, 2] (return 的值不会被展开)
// 4. throw 方法
function* errorGenerator() {
try {
yield 1;
yield 2;
} catch (e) {
console.log('Caught:', e);
yield 'recovered';
}
}
const gen4 = errorGenerator();
console.log(gen4.next()); // { value: 1, done: false }
console.log(gen4.throw(new Error('Test error')));
// 输出: Caught: Error: Test error
// 返回: { value: 'recovered', done: false }
3. Generator 与 Iterator 的关系
// Generator 自动实现 Iterator 接口
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = myGenerator();
// Generator 对象具有 Symbol.iterator 方法
console.log(typeof gen[Symbol.iterator]); // 'function'
// 并且返回自身
console.log(gen[Symbol.iterator]() === gen); // true
// 因此可以使用 for...of
for (const item of gen) {
console.log(item); // 1, 2, 3
}
深入理解
1. 自定义迭代器
// 实现一个 Range 类
class Range {
constructor(start, end, step = 1) {
this.start = start;
this.end = end;
this.step = step;
}
[Symbol.iterator]() {
let current = this.start;
const { end, step } = this;
return {
next() {
if (current <= end) {
const value = current;
current += step;
return { value, done: false };
}
return { done: true };
}
};
}
}
const range = new Range(1, 10, 2);
console.log([...range]); // [1, 3, 5, 7, 9]
// 使用 Generator 简化
class RangeWithGenerator {
constructor(start, end, step = 1) {
this.start = start;
this.end = end;
this.step = step;
}
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i += this.step) {
yield i;
}
}
}
const range2 = new RangeWithGenerator(1, 5);
console.log([...range2]); // [1, 2, 3, 4, 5]
2. 无限序列
// 使用 Generator 创建无限序列
function* infiniteSequence() {
let i = 0;
while (true) {
yield i++;
}
}
const infinite = infiniteSequence();
console.log(infinite.next().value); // 0
console.log(infinite.next().value); // 1
console.log(infinite.next().value); // 2
// ...
// 斐波那契数列
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacci();
console.log(fib.next().value); // 0
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3
console.log(fib.next().value); // 5
// 获取前 n 个斐波那契数
function take(n, iterable) {
const result = [];
const iterator = iterable[Symbol.iterator]();
for (let i = 0; i < n; i++) {
const { value, done } = iterator.next();
if (done) break;
result.push(value);
}
return result;
}
console.log(take(10, fibonacci()));
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
3. 异步迭代器
// 异步迭代器
const asyncIterable = {
[Symbol.asyncIterator]: async function* () {
yield new Promise(resolve => setTimeout(() => resolve(1), 1000));
yield new Promise(resolve => setTimeout(() => resolve(2), 1000));
yield new Promise(resolve => setTimeout(() => resolve(3), 1000));
}
};
// 使用 for await...of
async function consumeAsync() {
for await (const item of asyncIterable) {
console.log(item); // 1, 2, 3 (每秒一个)
}
}
// 异步 Generator
async function* asyncGenerator() {
const urls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
'https://api.example.com/data3'
];
for (const url of urls) {
const response = await fetch(url);
yield response.json();
}
}
// 使用
async function fetchAll() {
for await (const data of asyncGenerator()) {
console.log(data);
}
}
最佳实践
1. 迭代器模式应用
// 实现一个二叉树的中序遍历迭代器
class TreeNode {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
class BinaryTree {
constructor(root) {
this.root = root;
}
// 中序遍历迭代器
*[Symbol.iterator]() {
function* inorder(node) {
if (node) {
yield* inorder(node.left);
yield node.value;
yield* inorder(node.right);
}
}
yield* inorder(this.root);
}
}
// 构建树
const root = new TreeNode(4);
root.left = new TreeNode(2);
root.right = new TreeNode(6);
root.left.left = new TreeNode(1);
root.left.right = new TreeNode(3);
const tree = new BinaryTree(root);
console.log([...tree]); // [1, 2, 3, 4, 6]
2. 生成器实现控制流
// 使用 Generator 实现任务队列
function* taskQueue() {
const tasks = [];
while (true) {
const task = yield tasks.length;
if (task) {
tasks.push(task);
}
}
}
const queue = taskQueue();
queue.next(); // 初始化
console.log(queue.next(() => console.log('Task 1')).value); // 1
console.log(queue.next(() => console.log('Task 2')).value); // 2
// 执行所有任务
tasks.forEach(task => task());
// 状态机实现
function* stateMachine() {
let state = 'IDLE';
while (true) {
const event = yield state;
switch (state) {
case 'IDLE':
if (event === 'START') state = 'RUNNING';
break;
case 'RUNNING':
if (event === 'PAUSE') state = 'PAUSED';
if (event === 'STOP') state = 'STOPPED';
break;
case 'PAUSED':
if (event === 'RESUME') state = 'RUNNING';
if (event === 'STOP') state = 'STOPPED';
break;
case 'STOPPED':
if (event === 'RESET') state = 'IDLE';
break;
}
}
}
const machine = stateMachine();
machine.next(); // 初始化
console.log(machine.next('START').value); // 'RUNNING'
console.log(machine.next('PAUSE').value); // 'PAUSED'
console.log(machine.next('RESUME').value); // 'RUNNING'
console.log(machine.next('STOP').value); // 'STOPPED'
3. 惰性计算
// 惰性求值的管道操作
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;
}
}
}
function* take(iterable, n) {
let count = 0;
for (const item of iterable) {
if (count >= n) break;
yield item;
count++;
}
}
// 使用
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = take(
filter(
map(numbers, x => x * x),
x => x % 2 === 0
),
3
);
console.log([...result]); // [4, 16, 36]
// 实际只计算了前 6 个元素,而不是全部 10 个
4. 协程与异步控制
// 使用 Generator 实现 async/await 简化版
function run(generator) {
const iterator = generator();
function handle(result) {
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value)
.then(res => handle(iterator.next(res)))
.catch(err => handle(iterator.throw(err)));
}
return handle(iterator.next());
}
// 使用
function* fetchUserData() {
try {
const user = yield fetch('/api/user').then(r => r.json());
const posts = yield fetch(`/api/posts/${user.id}`).then(r => r.json());
return { user, posts };
} catch (error) {
console.error('Error:', error);
throw error;
}
}
// run(fetchUserData).then(data => console.log(data));
面试要点
- Iterator 接口:包含 next() 方法,返回 { value, done }
- 可迭代协议:实现
[Symbol.iterator]方法 - Generator:使用
function*定义,使用yield暂停执行 - 双向通信:通过
next(value)向 Generator 传值 - 应用场景:惰性计算、无限序列、异步流程控制、状态机
常见面试题
// 面试题 1:实现一个迭代器,遍历对象的深度属性
function* deepIterate(obj) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
if (typeof value === 'object' && value !== null) {
yield* deepIterate(value);
} else {
yield { key, value };
}
}
}
}
const nested = {
a: 1,
b: { c: 2, d: { e: 3 } },
f: 4
};
for (const item of deepIterate(nested)) {
console.log(item);
}
// { key: 'a', value: 1 }
// { key: 'c', value: 2 }
// { key: 'e', value: 3 }
// { key: 'f', value: 4 }
// 面试题 2:Generator 实现数组扁平化
function* flatten(array) {
for (const item of array) {
if (Array.isArray(item)) {
yield* flatten(item);
} else {
yield item;
}
}
}
const nestedArray = [1, [2, [3, 4], 5], 6];
console.log([...flatten(nestedArray)]); // [1, 2, 3, 4, 5, 6]
// 面试题 3:使用 Generator 实现 sleep 函数
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function* workflow() {
console.log('Start');
yield sleep(1000);
console.log('After 1s');
yield sleep(2000);
console.log('After 2s');
return 'Done';
}
// 执行
// run(workflow).then(result => console.log(result));
// 面试题 4:实现一个可迭代对象的组合
function* combine(...iterables) {
for (const iterable of iterables) {
yield* iterable;
}
}
const a = [1, 2, 3];
const b = new Set([4, 5, 6]);
const c = 'abc';
console.log([...combine(a, b, c)]);
// [1, 2, 3, 4, 5, 6, 'a', 'b', 'c']
// 面试题 5:Generator 和 Promise 的区别
// Generator:
// - 同步写法处理异步
// - 可以暂停和恢复
// - 需要执行器配合
// - 适合复杂流程控制
// Promise:
// - 真正的异步
// - 链式调用
// - 内置执行机制
// - 适合简单异步操作