JavaScript迭代器

181 阅读2分钟

概述

在JavaScript中,我们每天都在与各种数据集合打交道——数组、字符串、Map、Set等。而迭代器(Iterator)作为ES6引入的重要特性,为我们提供了一种统一、灵活的遍历机制。本文将带你深入理解迭代器的原理、应用场景以及如何自定义迭代器。

迭代器

迭代器是一种特殊对象,它提供统一的接口来遍历各种数据结构。无论数据是有序数组还是无序集合,迭代器都能提供一致的访问方式。

每个迭代器都有一个next()方法,每次调用返回一个包含valuedone属性的对象:

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}

可迭代协议与迭代器协议

可迭代协议

要实现可迭代,对象必须实现@@iterator方法(即Symbol.iterator属性),该方法返回一个迭代器对象。

迭代器协议

迭代器对象必须实现next()方法,返回{value: any, done: boolean}格式的对象。

内置可迭代对象

JavaScript中许多内置对象都是可迭代的:

// 数组
for (const item of [1, 2, 3]) {
  console.log(item);
}

// 字符串
for (const char of "hello") {
  console.log(char);
}

// Map
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
  console.log(key, value);
}

// Set
const set = new Set([1, 2, 3]);
for (const item of set) {
  console.log(item);
}

自定义迭代器

让我们创建一个简单的范围迭代器:

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

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

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

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

生成器函数

创建迭代器的语法糖,生成器函数是创建迭代器的更简洁方式,使用function*语法:

function* range(start, end, step = 1) {
  if (step > 0) {
    for (let i = start; i <= end; i += step) {
      yield i;
    }
  } else {
    for (let i = start; i >= end; i += step) {
      yield i;
    }
  }
}

// 使用生成器
for (const num of range(1, 5)) {
  console.log(num);
}

 异步迭代器

ES2018引入了异步迭代器,用于处理异步数据流:

async function* asyncSequence() {
  for (let i = 1; i <= 3; i++) {
    // 模拟异步操作
    await new Promise(resolve => setTimeout(resolve, 1000));
    yield i;
  }
}

(async () => {
  for await (const value of asyncSequence()) {
    console.log(value); // 每秒输出一个数字
  }
})();

最佳实践

1. 检测可迭代对象

function isIterable(obj) {
  return obj != null && typeof obj[Symbol.iterator] === 'function';
}

2. 组合多个迭代器

function* zip(...iterables) {
  const iterators = iterables.map(it => it[Symbol.iterator]());
  while (true) {
    const results = iterators.map(it => it.next());
    if (results.some(r => r.done)) break;
    yield results.map(r => r.value);
  }
}

const zipped = zip([1, 2, 3], ['a', 'b', 'c']);
console.log([...zipped]); // [[1, 'a'], [2, 'b'], [3, 'c']]