ES6/ES11知识点 续五
迭代器【Iterator】
ES6 中的**迭代器(Iterator)**是 JavaScript 的一种协议,它定义了对象如何被逐个访问。迭代器与 for…of、扩展运算符、解构赋值等语法密切相关。
📘 迭代器工作原理
ES6 迭代器的工作原理基于两个核心机制:
🌟 可迭代协议(Iterable Protocol)
如果一个对象实现了 Symbol.iterator 方法,并返回一个迭代器对象,那么它就是可迭代的(Iterable),可以用于:
- for…of
- 解构赋值(如 [a, b] = iterable)
- … 扩展运算符
- Array.from()
const iterable = {[Symbol.iterator]() {let i = 0;return {next() {return i < 3 ? { value: i++, done: false } : { done: true };}};}
};for (const val of iterable) {console.log(val); // 0 1 2
}
⚙️迭代器协议(Iterator Protocol)
迭代器对象必须具有 next() 方法,每次调用返回一个对象:
{ value: any, done: boolean }
含义:
- value: 当前值
- done: false: 迭代未结束
- done: true: 迭代结束
当 done: true 时,value 可选,将被忽略
🔄 for…of 背后的流程
等价于下面这个展开流程:
const iterator = iterable[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {const value = result.value;// 执行代码块result = iterator.next();
}
⸻
📦 内置可迭代对象如何实现?
例如数组:
const arr = [10, 20, 30];
const iter = arr[Symbol.iterator]();iter.next(); // { value: 10, done: false }
iter.next(); // { value: 20, done: false }
浏览器底层实现了数组的 Symbol.iterator 方法,它会返回一个迭代器对象。
🧠 迭代器的本质理解
- Symbol.iterator 是告诉 JavaScript:我可以被 for…of 遍历
- 返回的迭代器对象 next() 方法控制了“值从哪里来”和“什么时候结束”
- 你也可以用 yield(生成器)简化构建逻辑
🛠️ 手动创建一个迭代器
function createIterator(arr) {let index = 0;return {next() {if (index < arr.length) {return { value: arr[index++], done: false };} else {return { value: undefined, done: true };}}};
}const it = createIterator(['a', 'b', 'c']);
console.log(it.next()); // { value: 'a', done: false }
console.log(it.next()); // { value: 'b', done: false }
console.log(it.next()); // { value: 'c', done: false }
console.log(it.next()); // { value: undefined, done: true }
🚀 使对象可被 for…of 遍历
const iterableObj = {data: [10, 20, 30],[Symbol.iterator]() {let i = 0;const data = this.data;return {next() {return i < data.length? { value: data[i++], done: false }: { done: true };}};}
};for (const val of iterableObj) {console.log(val);
}
// 输出:10 20 30
📦 内置可迭代对象
类型 可迭代? 示例
数组 ✅ for (let x of [1,2,3])
字符串 ✅ for (let c of ‘abc’)
Set / Map ✅ for (let e of new Set())
arguments ✅ for (let a of arguments)
DOM NodeList ✅ for (let el of nodelist)
普通对象 ❌ { a: 1 } ❌不能直接 for…of
🧙♂️ 配合生成器使用(语法糖)
function* gen() {yield 1;yield 2;yield 3;
}
const g = gen();
console.log(g.next()); // { value: 1, done: false }for (const x of gen()) {console.log(x); // 1, 2, 3
}
✅ 判断是否可迭代
function isIterable(obj) {return typeof obj[Symbol.iterator] === 'function';
}
类数组对象添加自定义迭代器
方式一
const arrayLike = {0: 'a',1: 'b',2: 'c',length: 3,[Symbol.iterator]() {let index = 0;return {next: () => {return index < this.length? { value: this[index++], done: false }: { done: true };}};}
};for (const val of arrayLike) {console.log(val); // 输出:a, b, c
}
方式二
使用生成器方式更优雅
const arrayLike = {0: 'x',1: 'y',2: 'z',length: 3,*[Symbol.iterator]() {for (let i = 0; i < this.length; i++) {yield this[i];}}
};for (const val of arrayLike) {console.log(val); // 输出:x, y, z
}
生成器
在 ES6 中,生成器(Generator)是一种特殊的函数,能控制函数的执行流程,支持按需产出(惰性求值),适合处理迭代、异步、状态机等场景。
🧱 基本语法
function* myGenerator() {yield 1;yield 2;yield 3;
}
- 使用 function* 定义生成器函数(注意星号 *)。
- 内部使用 yield 表达式产生值。
- 调用生成器函数返回的是一个迭代器对象。
🚀 调用与执行流程
const gen = myGenerator();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 }
- 每次调用 next(),函数执行到下一个 yield 停下来,返回一个 { value, done } 对象。
- 最后 done: true 表示完成。
🔁 配合 for…of
for (const val of myGenerator()) {console.log(val); // 输出:1, 2, 3
}
🧠 生成器的高级用法
✅ 接收外部值
function* gen() {const a = yield 1;const b = yield a + 2;return b;
}const it = gen();console.log(it.next()); // { value: 1, done: false }
console.log(it.next(10)); // { value: 12, done: false }
console.log(it.next(100)); // { value: 100, done: true }
- yield 表达式可以接收传入的值,作为上一个 yield 的返回值。
🛠 典型应用场景
生成无限序列
function* idGenerator() {let id = 0;while (true) yield id++;
}const ids = idGenerator();
console.log(ids.next().value); // 0
console.log(ids.next().value); // 1
自定义迭代器
const obj = {*[Symbol.iterator]() {yield 1;yield 2;yield 3;}
};for (const x of obj) {console.log(x); // 输出:1, 2, 3
}
与异步协程结合(配合 async/await 或库如 co)
生成器可用于构造异步流程控制逻辑,尽管现在大多数场景被 async/await 替代。
yield* 的作用(委托子生成器)或生成器和 async/await 的区别
🔄 yield*:委托另一个生成器或可迭代对象
yield* 表达式可以将执行控制权交给另一个生成器或可迭代对象,就像“嵌套展开”。
✅ 示例 1:委托另一个生成器
function* inner() {yield 'a';yield 'b';
}function* outer() {yield 'start';yield* inner(); // 委托 inner 生成器yield 'end';
}for (const val of outer()) {console.log(val); // 输出:start, a, b, end
}
✅ 示例 2:委托数组
function* gen() {yield* [1, 2, 3];
}console.log([...gen()]); // [1, 2, 3]
⚙️ 生成器 vs async/await
特性 | 生成器(Generator) | async/await |
---|---|---|
关键字 | function*, yield | async function, await |
控制流程方式 | 手动通过 .next() 推进 | 自动推进 |
异步支持 | 不直接支持(需配合库) | 原生支持 Promise |
返回值 | 迭代器对象 | Promise |
适合场景 | 同步流程控制、数据生成 | 异步流程控制 |
🔧 示例对比
1️⃣ 生成器实现异步流程(配合库如 co)
function* asyncTask() {const data = yield fetch('...');console.log(data);
}
// 需要外部 runner 自动推进
2️⃣ async/await 简洁实现
async function asyncTask() {const data = await fetch('...');console.log(data);
}
异步生成器 async function* 的基本用法
async function* asyncGenerator() {let i = 0;while (i < 3) {await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟异步操作yield i++;}
}// 使用 for await...of 来消费异步生成器
(async () => {for await (const val of asyncGenerator()) {console.log(val); // 每秒输出一个数字:0, 1, 2}
})();
异步分页加载
async function* fetchPages(total) {for (let page = 1; page <= total; page++) {const data = await fetch(`https://api.example.com/data?page=${page}`);const json = await data.json();yield json.items;}
}(async () => {for await (const items of fetchPages(3)) {console.log('Page items:', items);}
})();
总结
- yield* 是 生成器中的“合并子迭代器”工具。
- async/await 是语法更清晰的异步生成器替代方案,自动推进,适合处理 Promise。
- 生成器仍适用于同步状态控制、迭代器构造、无限序列、DSL 构建等。