在本教程中,您将了解 JavaScript 迭代器以及如何使用迭代器更有效地处理数据序列。
for
循环问题
当你有一个数据数组时,你通常使用 for
循环来迭代它的元素。例如:
let ranks = ['A', 'B', 'C'];
for (let i = 0; i < ranks.length; i++) {
console.log(ranks[i]);
}
for
循环使用变量 i
来跟踪 ranks
数组的索引。每次循环执行时,只要 i
的值小于 ranks
数组中的元素数,它的 i
值就会递增。
这段代码很简单。但是,当您将一个循环嵌套在另一个循环中时,它的复杂性会增加。此外,跟踪循环内的多个变量很容易出错。
ES6 引入一种新的循环结构称为 for...of
,来消除标准 for
循环的复杂性并避免因跟踪 for
循环索引而导致的错误。
要迭代 ranks
数组的元素,您可以使用以 for...of
循环:
for(let rank of ranks) {
console.log(rank);
}
for...of
比 for
循环优雅得多,因为它展示代码的真正意图并遍历数组访问每个元素。
最重要的是,for...of
循环能够在任何可迭代对象上创建一个循环,而不仅仅是一个数组。要了解可迭代对象,您需要先了解迭代协议。
迭代协议
有两种迭代协议:iterable protocol 和 iterator protocol。
迭代器协议 iterator protocol
当一个对象实现下面两个接口时,它就是一个迭代器:
- 有没有剩下的元素?
- 如果有,元素是什么?
从技术上讲,当一个对象有一个 next()
方法返回具有以下两个属性的对象,它就被称为迭代器:
done
: 布尔值,指示是否还有可以迭代的元素。value
: 当前元素。
每次调用 next()
时,它都会返回集合中的下一个值:
{ value: 'next value', done: false }
如果在调用 next()
方法返回最后一个值,则 next()
方法返回结果对象如下:
{done: true: value: undefined}
属性 done
的值表示没有更多的值可返回,属性 value
的设置为 undefined
。
可迭代协议
当一个对象包含一个称为 [Symbol.iterator]
的方法,该不接受任何参数并返回一个符合迭代器协议的对象时,则该对象是可迭代的。
[Symbol.iterator]
是 ES6 内置的众所周知的符号之一。
迭代器
由于 ES6 为集合类型 Array
、Set
、Map
提供内置迭代器 ,因此您不必为这些对象创建迭代器。
如果您有自定义类型并希望使其可迭代可以使用 for...of
循环进行遍历,则需要实现迭代协议。
以下代码创建一个 Sequence 对象,该对象返回指定 start
, end
范围内的数字列表,后续数字之间有一个 interval
。
class Sequence {
constructor( start = 0, end = Infinity, interval = 1 ) {
this.start = start;
this.end = end;
this.interval = interval;
}
[Symbol.iterator]() {
let counter = 0;
let nextIndex = this.start;
return {
next: () => {
if ( nextIndex <= this.end ) {
let result = { value: nextIndex, done: false }
nextIndex += this.interval;
counter++;
return result;
}
return { value: counter, done: true };
}
}
}
};
以下代码在 for...of
循环中使用 Sequence
迭代器:
let evenNumbers = new Sequence(2, 10, 2);
for (const num of evenNumbers) {
console.log(num);
}
输出:
2
4
6
8
10
您可以显式访问 [Symbol.iterator]()
方法,如下所示:
let evenNumbers = new Sequence(2, 10, 2);
let iterator = evenNumbers[Symbol.iterator]();
let result = iterator.next();
while( !result.done ) {
console.log(result.value);
result = iterator.next();
}
迭代器 return 方法
除了 next()
方法之外,还可以选择 [Symbol.iterator]()
返回一个名为 return()
的方法。
当迭代过早停止时,将自动调用 return()
方法。您可以在其中放置清理资源的代码。
下面的示例实现 Sequence
对象的 return()
方法:
class Sequence {
constructor( start = 0, end = Infinity, interval = 1 ) {
this.start = start;
this.end = end;
this.interval = interval;
}
[Symbol.iterator]() {
let counter = 0;
let nextIndex = this.start;
return {
next: () => {
if ( nextIndex <= this.end ) {
let result = { value: nextIndex, done: false }
nextIndex += this.interval;
counter++;
return result;
}
return { value: counter, done: true };
},
return: () => {
console.log('cleaning up...');
return { value: undefined, done: true };
}
}
}
}
以下代码段使用 Sequence
对象生成从 1 到 10 的奇数序列。但是,它过早地停止迭代。最后,return()
方法被自动调用。
let oddNumbers = new Sequence(1, 10, 2);
for (const num of oddNumbers) {
if( num > 7 ) {
break;
}
console.log(num);
}
输出:
1
3
5
7
cleaning up...
结论
在本教程中,您了解了 JavaScript 迭代器以及如何使用迭代协议来实现自定义迭代逻辑。