返回首页

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));

面试要点

  1. Iterator 接口:包含 next() 方法,返回 { value, done }
  2. 可迭代协议:实现 [Symbol.iterator] 方法
  3. Generator:使用 function* 定义,使用 yield 暂停执行
  4. 双向通信:通过 next(value) 向 Generator 传值
  5. 应用场景:惰性计算、无限序列、异步流程控制、状态机

常见面试题

// 面试题 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:
// - 真正的异步
// - 链式调用
// - 内置执行机制
// - 适合简单异步操作