JavaScript 迭代器

深入理解 JavaScript 迭代器:从原理到实战

在现代前端开发中,我们经常使用 for...of、展开运算符 ...Array.from() 等语法,但你是否想过:为什么不同的数据结构都能用同一种方式遍历? 这背后的核心机制,就是 迭代器(Iterator)

本文将带你从零开始,全面掌握 JavaScript 中的迭代器,包括其设计思想、实现原理、常见应用和面试高频考点。


一、什么是迭代器?为什么需要它?

1.1 问题背景

JavaScript 有多种数据结构:

js 复制代码
const arr = [1, 2, 3];
const str = "hello";
const set = new Set([1, 2, 3]);
const map = new Map([['a', 1], ['b', 2]]);

它们的内部存储方式完全不同,但我们都希望用统一的方式遍历它们:

js 复制代码
for (const item of arr)  // 数组
for (const char of str)  // 字符串
for (const item of set)  // Set
for (const [k, v] of map) // Map

👉 如何实现这种"统一遍历"?答案就是:迭代器协议。


二、迭代器的核心:可迭代协议(Iterable Protocol)

2.1 两个关键概念

概念 说明
可迭代对象(Iterable) 实现了 [Symbol.iterator]() 方法的对象
迭代器(Iterator) .next() 方法的对象,返回 { value, done }

2.2 for...of 的工作原理

当你写:

js 复制代码
for (const item of arr) {
  console.log(item);
}

JavaScript 实际执行:

js 复制代码
const iterator = arr[Symbol.iterator](); // 获取迭代器
let result = iterator.next();

while (!result.done) {
  console.log(result.value);
  result = iterator.next();
}

2.3 手写一个迭代器

js 复制代码
function makeIterator(array) {
  let nextIndex = 0;
  return {
    next() {
      return nextIndex < array.length
        ? { value: array[nextIndex++], done: false }
        : { done: true };
    }
  };
}

const iter = makeIterator([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()); // { done: true }

三、让任意对象支持 for...of

3.1 为什么普通对象不能用 for...of

js 复制代码
const obj = { a: 1, b: 2 };
for (const item of obj) {
  // ❌ TypeError: obj is not iterable
}

因为普通对象没有实现 Symbol.iterator 方法。

3.2 让对象"可迭代"

js 复制代码
const obj = {
  a: 1,
  b: 2,
  [Symbol.iterator]() {
    const values = Object.values(this);
    let index = 0;
    return {
      next() {
        if (index < values.length) {
          return { value: values[index++], done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
};

for (const val of obj) {
  console.log(val); // 1, 2
}

四、生成器:迭代器的语法糖

手动写迭代器太繁琐?生成器函数(function* 可以自动帮你生成迭代器。

4.1 基本用法

js 复制代码
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

const iter = gen();
iter.next(); // { value: 1, done: false }
iter.next(); // { value: 2, done: false }

4.2 实现 range(1, 5)

js 复制代码
function* range(from, to) {
  for (let i = from; i <= to; i++) {
    yield i;
  }
}

for (const n of range(1, 3)) {
  console.log(n); // 1, 2, 3
}

五、yield*:委托给另一个可迭代对象

yield* 可以将遍历任务"委托"给另一个可迭代对象。

js 复制代码
function* gen() {
  yield* [1, 2];
  yield* 'ab';
  yield* new Set([3, 4]);
}

for (const x of gen()) {
  console.log(x); // 1, 2, 'a', 'b', 3, 4
}

六、哪些语法依赖迭代器?

任何接受"可迭代对象"的语法,都会调用其 Symbol.iterator 方法:

语法 示例
for...of for (const x of arr)
展开运算符 ... const newArr = [...arr]
数组解构 const [a, b] = arr
Array.from() Array.from(iterable)
new Map() / new Set() new Set([1,2,3])
Promise.all() Promise.all([p1, p2])

七、经典面试题:让对象支持数组解构

题目

js 复制代码
const [a, b] = { c: 1, d: 2 };
console.log(a, b); // 如何输出 1, 2?

解法

js 复制代码
Object.prototype[Symbol.iterator] = function* () {
  for (const key of Object.keys(this)) {
    yield this[key];
  }
};

const [a, b] = { c: 1, d: 2 };
console.log(a, b); // 1, 2 ✅

八、实际应用场景

8.1 惰性求值(Lazy Evaluation)

js 复制代码
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
// 可以无限生成,但只在需要时计算

8.2 合并有序数组(归并排序思想)

js 复制代码
function* mergeSorted(arr1, arr2) {
  let i = 0, j = 0;
  while (i < arr1.length && j < arr2.length) {
    yield arr1[i] < arr2[j] ? arr1[i++] : arr2[j++];
  }
  while (i < arr1.length) yield arr1[i++];
  while (j < arr2.length) yield arr2[j++];
}

console.log([...mergeSorted([1,3,5], [2,4,6])]); // [1,2,3,4,5,6]
相关推荐
XiaoSong2 小时前
从未有过如此丝滑的React Native开发体验:EAS开发构建完全指南
前端·react.js
掘金者阿豪2 小时前
打通KingbaseES与MyBatis:一篇详尽的Java数据持久化实践指南
前端·后端
RoyLin3 小时前
TypeScript设计模式:原型模式
前端·后端·node.js
我是天龙_绍3 小时前
vue Composables 组合式函数
前端
zjjuejin3 小时前
Maven项目的核心蓝图:POM文件
前端·maven
小气小憩3 小时前
“暗战”百度搜索页:Monica悬浮球被“围剿”,一场AI Agent与传统巨头的流量攻防战
前端·人工智能
前端付豪3 小时前
1、震惊!99% 前端都没搞懂的 JavaScript 类型细节
前端·javascript·面试
朝与暮3 小时前
js符号(Symbol)
前端·javascript
恋猫de小郭4 小时前
对于普通程序员来说 AI 是什么?AI 究竟用的是什么?
前端·flutter·ai编程