今天我们来学习JavaScript中的Iterator
(迭代器),它是个什么东西?能干什么事情?
for循环的问题
假设我们有一个数组,需要使用数组中的每一个元素,我们就需要写个for循环,像下面这样
let ranks = ['A', 'B', 'C'];
for (let i = 0; i < ranks.length; i++) {
console.log(ranks[i]);
}
使用for循环要先定义个变量i
来作为数组的索引,只要i
不超过数组元素的数量,每次i
都会自增。
这段代码很简单,当时当我们需要循环套循环的时候,复杂性就会成倍增加,需要声明多个i
变量作为数组的索引。
ES6引入了for...of
新的循环结构,用来解决for循环的这些问题。
下面使用for...of
的方式来循环这个数组
let ranks = ['A', 'B', 'C'];
for(let rank of ranks) {
console.log(rank);
}
相比for循环来说,使用 for...of
更加的优雅,省去了很多无用的代码,语义性也更强。
for...of
不仅可以循环数组,同时也能够支持在任何支持iterator对象上创建循环。
要了解iterator对象,首先我们要了解iterator
的接口/协议。
iterable 接口/协议
有两种协议接口,分别为iterator
和iterable
Iterator 接口/协议
当一个对象实现下面的功能接口时,那它就是一个迭代器
- 还有剩下的元素吗?
- 如果有,元素是什么?
从技术上说,当一个对象有一个next()
方法并返回下面两个属性时,它就是迭代器
done
: 一个布尔值,代表是否还有下一个元素,如果有就是true,反之为falsevalue
: 当前值
每次调用 next()
时,它都会返回集合中的下一个值
{
value: 'next value', done: false }
如果返回的是最后一个值
{
done: true: value: undefined}
done
为true表示集合没有下一个值返回,并且value为undefined
。
Iterable 接口
当一个对象含有[Symbol.iterator]
时,它就是可迭代的,该方法不接受任何参数并返回一个符合迭代器协议的对象。
[Symbol.iterator]
时ES6中的一个well-known symbol,更多well-known symbol,可以看昨天的文章。
Iterator
在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 };
}
}
}
};
下面我们来new一个Sequence
对象,取出2~10之间的所有偶数,start
, end
分别为2、10,interval
累加值为2。
由于我们实现了[Symbol.iterator]
迭代器接口,所以我们可以使用 for...of
来循环迭代它
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();
}
// 输出:
// 2
// 4
// 6
// 8
// 10
return() 方法
除了next()
方法之外,[Symbol.iterator]()
也可以返回一个return()
方法。
当迭代被中断时,会自动调用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('清理...');
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
// 清理...
总结
今天我们学习JavaScript的iterator迭代器,以及如何使用迭代接口来实现自定义的迭代逻辑。
如果本文有帮助,微信搜索【小帅的编程笔记】,让我们每天进步