今天我们来学习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迭代器,以及如何使用迭代接口来实现自定义的迭代逻辑。
如果本文有帮助,微信搜索【小帅的编程笔记】,让我们每天进步
本文介绍了JavaScript中的Iterator迭代器和Iterable可迭代接口,解释了它们如何简化for循环,特别是for...of循环的使用。文章通过示例展示了如何自定义可迭代对象,并讨论了return()方法在迭代中断时的作用。最后,通过一个实际例子展示了如何利用Iterator实现特定逻辑的循环。
1114

被折叠的 条评论
为什么被折叠?



