一、基础概念分层理解
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 }
二、底层运作示意图
三、自定义迭代器的两种方式
方式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* + yield | function* 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;
八、练习题
-
实现一个
reverseIterable(arr),使其能逆序遍历数组:const arr = [1, 2, 3]; for (const num of reverseIterable(arr)) { console.log(num); // 应输出 3, 2, 1 } -
用生成器函数创建一个无限循环的迭代器,轮流返回
'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'
// 无限交替下去...