各位观众老爷,晚上好!今天咱们聊聊JavaScript里一个挺有意思的东西:Iterable 协议。这玩意儿说白了,就是让你自己定义的 JavaScript 对象也能像数组一样,用 for...of 循环来遍历。听起来是不是挺酷炫的?
咱们先打个预防针,这玩意儿初学可能会觉得有点绕,但只要你跟着我一步一步走,保证你能掌握。而且,掌握了它,你在 JavaScript 的江湖地位,绝对能提升一个档次。
开场白:for...of 的诱惑
在 JavaScript 的世界里,我们经常需要遍历一些数据结构。比如数组:
const myArray = [1, 2, 3, 4, 5];
for (const element of myArray) {
console.log(element); // 输出 1, 2, 3, 4, 5
}
这个 for...of 循环用起来是不是感觉特别舒服?简洁明了,比传统的 for 循环和 forEach 方便多了。
但是,如果你想让自己的对象也能用 for...of 循环,那就需要用到今天的主角:Iterable 协议了。
什么是 Iterable 协议?
Iterable 协议其实很简单,它规定了一个对象要能够被 for...of 循环遍历,必须满足两个条件:
-
必须有一个
Symbol.iterator属性。 这个属性的值是一个函数,这个函数返回一个迭代器(Iterator)对象。 -
迭代器对象必须有一个
next()方法。 这个next()方法返回一个对象,这个对象包含两个属性:value和done。value:表示当前迭代的值。done:表示迭代是否结束。如果迭代结束,done的值为true,否则为false。
是不是有点抽象?别怕,咱们用一个表格来总结一下:
| 协议 | 说明 |
|---|---|
Iterable |
定义了对象如何被 for...of 循环遍历。 |
Symbol.iterator |
对象的一个属性,其值是一个函数,返回一个迭代器对象。 |
Iterator |
由 Symbol.iterator 返回的对象,必须包含 next() 方法。 |
next() |
迭代器对象的方法,返回一个对象,包含 value 和 done 属性。 |
value |
next() 方法返回的对象的一个属性,表示当前迭代的值。 |
done |
next() 方法返回的对象的一个属性,表示迭代是否结束。 true 表示迭代结束, false 表示迭代未结束。 |
举个栗子:自定义一个可迭代对象
光说不练假把式,咱们来写一个例子,让大家更直观地理解 Iterable 协议。
假设我们有一个 Range 类,表示一个数字范围。我们希望能够用 for...of 循环来遍历这个范围内的所有数字。
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let currentValue = this.start;
const that = this; // 避免 `this` 指向问题
return {
next() {
if (currentValue <= that.end) {
return { value: currentValue++, done: false };
} else {
return { value: undefined, done: true };
}
},
};
}
}
const myRange = new Range(1, 5);
for (const number of myRange) {
console.log(number); // 输出 1, 2, 3, 4, 5
}
咱们来一步一步分析一下这段代码:
-
[Symbol.iterator]()方法: 这是实现Iterable协议的关键。这个方法返回一个迭代器对象。 -
迭代器对象: 这个对象包含一个
next()方法。 -
next()方法: 这个方法返回一个对象,包含value和done属性。value:表示当前迭代的值,也就是currentValue。done:表示迭代是否结束。当currentValue大于this.end时,迭代结束,done的值为true。
更简洁的写法:使用生成器函数
上面的代码稍微有点繁琐,我们可以使用生成器函数来简化代码。生成器函数是 ES6 引入的一个新特性,它可以更方便地创建迭代器。
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i++) {
yield i;
}
}
}
const myRange = new Range(1, 5);
for (const number of myRange) {
console.log(number); // 输出 1, 2, 3, 4, 5
}
在这个版本中,我们使用了 * 符号来定义一个生成器函数。生成器函数使用 yield 关键字来产生值。
yield i 相当于:
return { value: i, done: false };
当循环结束时,生成器函数会自动返回:
return { value: undefined, done: true };
是不是感觉清爽多了?
Iterable 协议的应用场景
Iterable 协议的应用场景非常广泛,比如:
- 自定义数据结构: 你可以用它来让你的自定义数据结构支持
for...of循环。 - 异步数据流: 你可以用它来处理异步数据流,比如从服务器获取数据。
- 惰性计算: 你可以用它来实现惰性计算,也就是只有在需要的时候才计算值。
再来几个例子:
- 斐波那契数列:
class Fibonacci {
constructor(max) {
this.max = max;
}
*[Symbol.iterator]() {
let a = 0;
let b = 1;
let count = 0;
while (count < this.max) {
yield a;
[a, b] = [b, a + b];
count++;
}
}
}
const fibonacciSequence = new Fibonacci(10);
for (const number of fibonacciSequence) {
console.log(number); // 输出斐波那契数列的前10项
}
- 无限循环的字母表:
class Alphabet {
*[Symbol.iterator]() {
let charCode = 'A'.charCodeAt(0);
while (true) {
yield String.fromCharCode(charCode);
charCode++;
if (charCode > 'Z'.charCodeAt(0)) {
charCode = 'A'.charCodeAt(0);
}
}
}
}
const alphabet = new Alphabet();
let count = 0;
for (const letter of alphabet) {
console.log(letter);
count++;
if (count > 25) { // 限制循环次数
break;
}
}
Iterable 协议与 Iterator 协议
Iterable 协议和 Iterator 协议经常一起出现,它们是密切相关的。
Iterable协议定义了如何获取一个迭代器。Iterator协议定义了迭代器应该如何工作。
一个对象要能够被 for...of 循环遍历,必须同时满足这两个协议。
内置的可迭代对象
JavaScript 内置了很多可迭代对象,比如:
ArrayStringMapSetarguments对象NodeList(DOM 节点列表)
这些对象都实现了 Iterable 协议,所以可以直接用 for...of 循环来遍历。
for...in vs for...of
很多同学容易把 for...in 和 for...of 搞混,它们之间有很大的区别:
for...in循环遍历的是对象的属性名(key)。for...of循环遍历的是对象的属性值(value)。
const myObject = { a: 1, b: 2, c: 3 };
for (const key in myObject) {
console.log(key); // 输出 "a", "b", "c"
}
// myObject 不是 Iterable 对象,直接使用 for...of 会报错
// for (const value of myObject) {
// console.log(value);
// }
总结
Iterable 协议是 JavaScript 中一个非常重要的概念,它可以让你自定义的对象支持 for...of 循环。掌握了它,你可以写出更简洁、更优雅的代码。
咱们再用一个表格来总结一下今天的内容:
| 概念 | 说明 |
|---|---|
Iterable 协议 |
定义了对象如何被 for...of 循环遍历。 |
Symbol.iterator |
对象的一个属性,其值是一个函数,返回一个迭代器对象。 |
Iterator 协议 |
定义了迭代器应该如何工作。 |
next() 方法 |
迭代器对象的方法,返回一个对象,包含 value 和 done 属性。 |
value |
next() 方法返回的对象的一个属性,表示当前迭代的值。 |
done |
next() 方法返回的对象的一个属性,表示迭代是否结束。 true 表示迭代结束, false 表示迭代未结束。 |
| 生成器函数 | 可以更方便地创建迭代器。使用 yield 关键字来产生值。 |
for...in vs for...of |
for...in 循环遍历的是对象的属性名(key),for...of 循环遍历的是对象的属性值(value)。 |
希望今天的讲座对大家有所帮助。 记住,编程之路没有捷径,只有不断学习和实践,才能成为真正的编程高手。
下次再见!