阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 迭代器模式(Iterator)与生成器函数的关系

迭代器模式(Iterator)与生成器函数的关系

作者:陈川 阅读数:2467人阅读 分类: JavaScript

迭代器模式是一种行为设计模式,允许顺序访问聚合对象的元素而不暴露其底层表示。生成器函数是JavaScript中实现迭代器的语法糖,二者在遍历数据时存在紧密关联,但实现方式和适用场景各有特点。

迭代器模式的核心概念

迭代器模式由两个关键部分组成:可迭代对象(Iterable)和迭代器(Iterator)。在JavaScript中,任何实现了[Symbol.iterator]方法的对象都是可迭代对象。当调用该方法时,会返回一个迭代器对象,该对象必须实现next()方法。

const array = [1, 2, 3];
const iterator = array[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 }

手动实现迭代器模式的典型例子:

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 { value: current++, done: false };
        }
        return { done: true };
      }
    };
  }
}

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

生成器函数的运作机制

生成器函数通过function*语法声明,调用时返回一个生成器对象(也是迭代器)。其特殊之处在于可以使用yield暂停执行并返回中间值:

function* generateSequence() {
  yield 1;
  yield 2;
  return 3;
}

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

生成器函数可以大大简化迭代器的实现。之前的Range类用生成器重写:

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

  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
  }
}

二者在异步编程中的差异

迭代器模式通常用于同步数据遍历,而生成器结合yield可以暂停执行,这使其在异步编程中表现出独特优势:

function* asyncGenerator() {
  const result1 = yield fetch('https://api.example.com/data1');
  const data1 = yield result1.json();
  
  const result2 = yield fetch(`https://api.example.com/data2?id=${data1.id}`);
  return yield result2.json();
}

// 需要配合执行器使用
async function runGenerator(generator) {
  const iterator = generator();
  let result = iterator.next();
  
  while (!result.done) {
    try {
      const value = await result.value;
      result = iterator.next(value);
    } catch (err) {
      result = iterator.throw(err);
    }
  }
  
  return result.value;
}

内存效率对比

生成器函数在处理大型数据集时具有明显的内存效率优势,因为它们支持惰性求值(Lazy Evaluation):

function* generateLargeArray() {
  for (let i = 0; i < 1e6; i++) {
    yield i;
  }
}

// 不会立即占用内存
const largeArrayIterator = generateLargeArray();

相比之下,传统数组迭代需要预先生成完整数据集:

function createLargeArray() {
  const arr = [];
  for (let i = 0; i < 1e6; i++) {
    arr.push(i);
  }
  return arr;
}

// 立即占用大量内存
const largeArray = createLargeArray();

组合使用的典型案例

生成器函数可以相互组合,形成复杂的数据处理管道:

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

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

const numbers = [1, 2, 3, 4, 5];
const processed = map(
  x => x * 2,
  filter(x => x % 2 === 0, numbers)
);

console.log([...processed]); // [4, 8]

错误处理机制比较

迭代器实现通常需要手动处理错误:

class SafeRange {
  constructor(start, end) {
    if (start > end) throw new Error('Invalid range');
    this.start = start;
    this.end = end;
  }

  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    
    return {
      next() {
        if (current > end) {
          throw new Error('Iteration exceeded bounds');
        }
        return { value: current++, done: false };
      }
    };
  }
}

生成器函数则可以利用try-catch实现更直观的错误处理:

function* safeGenerate(start, end) {
  if (start > end) throw new Error('Invalid range');
  
  try {
    for (let i = start; i <= end; i++) {
      if (i > end * 2) {
        throw new Error('Iteration exceeded bounds');
      }
      yield i;
    }
  } catch (err) {
    console.error('Generator error:', err);
    throw err;
  }
}

与语言特性的深度集成

JavaScript语言本身对生成器有深度支持,如yield*语法可以委托给另一个生成器:

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

function* gen2() {
  yield 1;
  yield* gen1();
  yield 4;
}

console.log([...gen2()]); // [1, 2, 3, 4]

这种特性使得生成器比传统迭代器更适合构建复杂的控制流。例如实现递归遍历:

function* traverseTree(node) {
  if (!node) return;
  yield node.value;
  yield* traverseTree(node.left);
  yield* traverseTree(node.right);
}

状态保持能力

生成器函数会自动维护执行状态,而手动实现的迭代器需要显式管理状态:

// 传统迭代器需要手动保存状态
class StatefulIterator {
  constructor() {
    this.state = 0;
  }

  next() {
    return {
      value: this.state++,
      done: false
    };
  }
}

// 生成器自动维护状态
function* statefulGenerator() {
  let state = 0;
  while (true) {
    yield state++;
  }
}

浏览器API中的应用实例

现代浏览器API如Web Locks API也采用了类似的模式:

async function* lockedResources(resources) {
  for (const resource of resources) {
    const lock = await navigator.locks.request(resource.id, async lock => {
      return lock;
    });
    yield { resource, lock };
  }
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌