JS 迭代器是什么东西
为什么我们能用
for...of
遍历数组?为什么展开运算符...
能把对象展开?这一切都源于 JavaScript 中的迭代器机制。
迭代器:数据遍历的秘密武器
想象一下,你在图书馆看书,图书管理员一本本把书递给你,直到没有更多书为止------这就是迭代器的工作方式。迭代器(Iterator) 是 JavaScript 中一种特殊的对象,它提供了一种统一的机制来遍历各种数据结构。
迭代器协议
迭代器遵循一个简单的协议:它必须实现一个 next()
方法,每次调用返回一个包含两个属性的对象:
yaml
JavaScript
{
value: 当前值,
done: 是否迭代完成
}
来看一个最简迭代器实现:
lua
JavaScript
function createCounter(max) {
let count = 0;
return {
next() {
if (count < max) {
return { value: count++, done: false };
}
return { value: undefined, done: true };
}
};
}
const counter = createCounter(3);
console.log(counter.next()); // { value: 0, done: false }
console.log(counter.next()); // { value: 1, done: false }
console.log(counter.next()); // { value: 2, done: false }
console.log(counter.next()); // { value: undefined, done: true }
这个计数器每次调用 next()
返回递增数字,直到达到最大值。
可迭代对象:迭代器的生产工厂
要使数据结构可遍历,它必须是可迭代对象(Iterable) 。可迭代对象必须实现 @@iterator
方法(通过 Symbol.iterator
访问):
kotlin
JavaScript
const myIterable = {
data: [10, 20, 30],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
}
return { done: true };
}
};
}
};
// 现在可以用 for...of 遍历了
for (const value of myIterable) {
console.log(value); // 依次输出 10, 20, 30
}
内置可迭代对象
JavaScript 中许多内置对象原生支持迭代:
arduino
JavaScript
// 数组
const arr = [1, 2, 3];
for (const num of arr) {
console.log(num);
}
// 字符串
const str = "你好";
for (const char of str) {
console.log(char); // 输出 "你" 和 "好"
}
// Map
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
console.log(key, value);
}
// NodeList
document.querySelectorAll('div').forEach(node => {
// NodeList 也是可迭代的
});
迭代器的实际应用场景
1. 自定义数据结构的遍历
ini
JavaScript
class Matrix {
constructor(width, height, content = []) {
this.width = width;
this.height = height;
this.content = content;
}
[Symbol.iterator]() {
let row = 0;
let col = 0;
return {
next: () => {
if (row === this.height) {
return { done: true };
}
const value = {
x: col,
y: row,
value: this.content[row * this.width + col]
};
col++;
if (col === this.width) {
col = 0;
row++;
}
return { value, done: false };
}
};
}
}
const matrix = new Matrix(2, 2, [1, 2, 3, 4]);
for (const {x, y, value} of matrix) {
console.log(`(${x},${y}): ${value}`);
}
// (0,0): 1
// (1,0): 2
// (0,1): 3
// (1,1): 4
2. 解构赋值与展开运算符
迭代器让这些操作成为可能:
csharp
JavaScript
// 数组解构
const [first, second] = [10, 20, 30];
// 字符串解构
const [a, b] = "AB";
// 展开运算符
const newArray = [...'hello']; // ['h','e','l','l','o']
3. 异步迭代器(高级特性)
ES2018 引入了异步迭代器,用于处理异步数据流:
javascript
JavaScript
async function* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
(async () => {
for await (const value of asyncGenerator()) {
console.log(value); // 1, 2, 3
}
})();
迭代器使用注意事项
-
单向性:迭代器是一次性用品,遍历后不能重置
rubyJavaScript const arr = [1, 2, 3]; const it = arr[Symbol.iterator](); it.next(); // { value: 1, done: false } it.next(); // { value: 2, done: false } it.next(); // { value: 3, done: false } it.next(); // { done: true } // 无法重置,需要重新获取迭代器
-
部分消费:可以只使用部分迭代结果
javascriptJavaScript function* generator() { yield 1; yield 2; yield 3; } const [first] = generator(); // first = 1
-
无限迭代器:注意避免无限循环
javascriptJavaScript function* infinite() { let index = 0; while (true) { yield index++; } } // 使用时要加入中断条件 for (const num of infinite()) { if (num > 5) break; console.log(num); }
迭代器 vs 类数组
常见误区是把类数组当作可迭代对象:
javascript
JavaScript
// 类数组对象(有 length 和索引)
const arrayLike = {
0: 'a',
1: 'b',
length: 2
};
// 错误!类数组不可迭代
// for (const item of arrayLike) { ... }
// 解决方案:
// 1. 转换为数组
const realArray = Array.from(arrayLike);
// 2. 添加迭代器
arrayLike[Symbol.iterator] = function() {
let index = 0;
return {
next: () => {
return index < this.length
? { value: this[index++], done: false }
: { done: true };
}
};
};
总结
JS 迭代器是 JavaScript 中强大的抽象机制:
- 提供统一的遍历接口(
for...of
,...
等) - 让自定义数据结构具备可迭代能力
- 支持异步数据流处理(异步迭代器)
- 广泛应用于数组操作、解构赋值等场景
理解迭代器不仅能让你写出更优雅的代码,还能揭开 JavaScript 内部运作的神秘面纱。当你下次使用 for...of
循环时,不妨想想背后默默工作的迭代器对象!
迭代器不只是工具,更是连接数据与操作的桥梁。掌握它,让你的 JavaScript 之旅更上一层楼。