迭代器

75 阅读4分钟

一、基础概念分层理解

1. 可迭代对象(Iterable)

  • 定义:实现了 [Symbol.iterator] 方法的对象
  • 特征
    • 可以用 for...of 循环遍历
    • 可以使用扩展运算符 ... 和解构赋值
  • 常见内置可迭代对象
    Array, String, Map, Set, TypedArray, arguments 对象等
    

2. 迭代器(Iterator)

  • 定义:一个实现了 next() 方法的对象 (一个能按顺序访问集合中的元素,并记录当前遍历的位置的对象)
  • 特征
    • next() 返回 { value: any, done: boolean }
    • 遍历完成后 done 变为 true
  • 手动控制示例
    const arr = [10, 20];
    const iterator = arr[Symbol.iterator](); // 获取迭代器
    
    console.log(iterator.next()); // { value: 10, done: false }
    console.log(iterator.next()); // { value: 20, done: false }
    console.log(iterator.next()); // { value: undefined, done: true }
    

二、底层运作示意图

image.png


三、自定义迭代器的两种方式

方式1:直接实现迭代器协议

// 创建一个可迭代对象
const range = {
  start: 1,
  end: 3,
  [Symbol.iterator]() {
    let current = this.start;
    return { // 返回迭代器对象
      next: () => {
        if (current <= this.end) {
          return { value: current++, done: false };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};

// 使用 for...of 遍历
for (const num of range) {
  console.log(num); // 1, 2, 3
}

// 使用
const iterator = range[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 }

方式2:用生成器函数(简化迭代器)

function*yield 快速创建迭代器:

// 生成器函数自动返回迭代器
function* rangeGenerator(start, end) {
  for (let i = start; i <= end; i++) {
    yield i;
  }
}

// 使用示例
const gen = rangeGenerator(1, 3);
console.log(gen.next().value); // 1
console.log([...gen]); // [2, 3](注意:迭代器已部分消耗)

四、关键区别澄清

概念职责方法/属性示例
可迭代对象提供迭代器[Symbol.iterator]()arr = [1,2,3]
迭代器逐个返回值next()arr[Symbol.iterator]()
生成器快速创建迭代器的语法糖function* + yieldfunction* gen() { yield 1 }

五、其他特性

1:对象默认不可迭代

// 错误:普通对象不是可迭代对象
const obj = { a: 1, b: 2 };
for (const item of obj) {} // TypeError: obj is not iterable

// 正确:需手动添加迭代器
obj[Symbol.iterator] = function* () {
  for (const key in this) {
    yield [key, this[key]];
  }
};
console.log([...obj]); // [ ['a', 1], ['b', 2] ]

2:迭代器是一次性的,无法重置,需重新获取

const arr = [1, 2];
const iter = arr[Symbol.iterator]();

console.log([...iter]); // [1, 2]
console.log([...iter]); // [] (迭代器已耗尽)

// 重新获取迭代器
const newIter = arr[Symbol.iterator]();
console.log([...newIter]); // [1, 2]

3:迭代器的惰性求值(Lazy Evaluation)

需要时才生成值(适合处理大数据或无限序列)

  • 特点:仅在调用 next() 时计算值

  • 优势:处理大规模数据时节省内存

  • 示例:无限序列

    function* infiniteNumbers() {
      let n = 0;
      while (true) {
        yield n++;
      }
    }
    const numbers = infiniteNumbers();
    console.log(numbers.next().value); // 0
    console.log(numbers.next().value); // 1
    // 不会一次性生成所有数字!
    

4:迭代器状态独立

  • 规则:每次调用 [Symbol.iterator]() 会生成新的迭代器

    const arr = [1, 2];
    const iter1 = arr[Symbol.iterator]();
    const iter2 = arr[Symbol.iterator]();
    
    iter1.next(); // { value: 1 }
    iter2.next(); // { value: 1 } (互不影响)
    

六、实际应用场景

1. 分页加载数据

async function* fetchPages(url) {
  let page = 1;
  while (true) {
    const res = await fetch(`${url}?page=${page}`);
    const data = await res.json();
    if (data.length === 0) break;
    yield data;
    page++;
  }
}

// 使用
const pageLoader = fetchPages('https://api.example/items');
for await (const pageData of pageLoader) {
  console.log('Loaded page:', pageData);
}

2.自定义数据结构遍历

class Stack {
  constructor() {
    this.items = [];
  }
  push(item) { this.items.push(item); }
  pop() { return this.items.pop(); }
  // 实现迭代器
  [Symbol.iterator]() {
    let index = this.items.length - 1; // 逆序遍历
    return {
      next: () => ({
        value: this.items[index--],
        done: index < -1
      })
    };
  }
}

const stack = new Stack();
stack.push(3);
stack.push(2);
stack.push(1);
console.log([...stack]); // [1, 2, 3]

3. 遍历树结构

class TreeNode {
  constructor(value, children = []) {
    this.value = value;
    this.children = children;
  }

  *[Symbol.iterator]() { // 深度优先遍历
    yield this.value;
    for (const child of this.children) {
      yield* child; // 递归 yield*
    }
  }
}

// 使用
const tree = new TreeNode(1, [
  new TreeNode(2, [new TreeNode(4)]),
  new TreeNode(3)
]);
console.log([...tree]); // [1, 2, 4, 3]

4.逐行读取文件

// 模拟文件流
function* readLines(file) {
  let line = 0;
  while (line < file.totalLines) {
    yield `Line ${line + 1}: ${file.content[line]}`;
    line++;
  }
}

const file = { content: ['Hello', 'World'], totalLines: 2 };
for (const line of readLines(file)) {
  console.log(line); // 逐行输出
}

5.生成无限序列(如斐波那契数列)

 function* fibonacci() {
   let a = 0, b = 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

七、 与其他语法的结合

  • 扩展运算符 ...

    javascript
    const arr = [...myIterable]; // 将可迭代对象转为数组
    
  • 解构赋值

    javascript
    const [first, second] = myIterable;
    

八、练习题

  1. 实现一个 reverseIterable(arr),使其能逆序遍历数组:

    const arr = [1, 2, 3];
    for (const num of reverseIterable(arr)) {
      console.log(num); // 应输出 3, 2, 1
    }
    
  2. 用生成器函数创建一个无限循环的迭代器,轮流返回 'A''B'

    const alternator = alternateAB();
    console.log(alternator.next().value); // 'A'
    console.log(alternator.next().value); // 'B'
    console.log(alternator.next().value); // 'A'
    
答案一:

法1:使用生成器函数

function* reverseIterable(arr) {
  // 从数组末尾开始逆序遍历
  for (let i = arr.length - 1; i >= 0; i--) {
    yield arr[i];
  }
}

// 使用示例
const arr = [1, 2, 3];
for (const num of reverseIterable(arr)) {
  console.log(num); // 3, 2, 1
}

法2:手动实现迭代器协议

const reverseIterable = {
  [Symbol.iterator]: function() {
    let index = this.data.length - 1; // 从末尾开始
    return {
      next: () => {
        if (index >= 0) {
          return { value: this.data[index--], done: false };
        } else {
          return { done: true };
        }
      }
    };
  },
  data: [1, 2, 3] // 假设数据直接挂载在对象上(实际可动态传入)
};

// 使用示例
for (const num of reverseIterable) {
  console.log(num); // 3, 2, 1
}
答案二:
function* alternateAB() {
  let isA = true; // 初始状态为 'A'
  while (true) {  // 无限循环
    yield isA ? 'A' : 'B';
    isA = !isA;   // 切换状态
  }
}

// 使用示例
const alternator = alternateAB();
console.log(alternator.next().value); // 'A'
console.log(alternator.next().value); // 'B'
console.log(alternator.next().value); // 'A'
console.log(alternator.next().value); // 'B'
// 无限交替下去...