javascript中的迭代器

360 阅读3分钟

「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」 本文摘要:本博文主要介绍了javascript中迭代器的概念及内置类型的迭代器原理,对象的自定义迭代器实现,以及迭代器中途退出的现象,各个知识点附有代码可供参考。

1.可迭代对象iterable

可迭代对象为实现了iterable接口的对象,可以通过迭代器去使用(消费)。迭代会在一个有序集合上进行(“有序”可以理解为集合中所有项都可以按照既定的顺序被遍历到,特别是开始和结束项有明确的定义。)在ECMAScript中实现iterable接口的对象使用Symbol.Iterator属性作为它的默认迭代器, 这个默认迭代器属性是一个迭代器工厂函数,调用这个工厂函数则返回一个迭代器。

1.1很多内置类型也都实现了iterable接口如String、Array、TypedArray、Map、WeakMap、Set 和 WeakSet。 我们可以通过简单的的调用Symbol.iterator来看该对象是否实现iterable接口。示例代码如下:

let a1 = [1,2,3,4,5];
let a2 = new Set([1,2,3,4]);
let a3 = new Map();
a3.set(1,123);
console.log(a1[Symbol.iterator]());   //Object [Array Iterator] {}
console.log(a2[Symbol.iterator]());   //[Set Iterator] { 1, 2, 3, 4 }
console.log(a3[Symbol.iterator]());   //[Map Entries] { [ 1, 123 ] }

1.2迭代器使用next()方法来遍历数据,该方法返回一个IteratorResult对象,该对象有两个属性done和value,每调用一次next()方法,当前迭代器的位置会向后移动一个元素。done为true,表示此迭代器以遍历到结尾,此时value为undefined。示例代码如下:

let a4 = [1,2,3];
let it1 = a4[Symbol.iterator]();
console.log(it1.next());   //{ value: 1, done: false }
console.log(it1.next());   // { value: 2, done: false }
console.log(it1.next());   //{ value: 3, done: false }
console.log(it1.next());   //{ value: undefined, done: true }

1.3实际使用过程中,我们不需要单独的调用此工厂函数来获得迭代器,js会自动提取出迭代器,如下代码。

let a5 = [1,2,3];
for(let x of a5){
    console.log(x
}
/*输出结果为:
1
2
3
*/

1.4不同的默认迭代器直接没有关联,是相互独立的;另外,可迭代对象在迭代期间发生变化,迭代器得出的value也会相应的变化。这两个特点的使用可参考如下代码,a2和a3为两个迭代器,游标所在位置互不影响:

let a6 = [1,2,3];
let it2 = a6[Symbol.iterator]();
let it3 = a6[Symbol.iterator]();
console.log(it2.next().value);        //1
console.log(it2.next().value);        //2
a6[2] = 4;
console.log(it2.next().value);        //4
console.log(it3.next().value);        //1

2.对象的自定义迭代器(用实现iterator接口实现)

由第一部分可以知道,任何实现 Iterator 接口的对象都可以作为迭代器使用,所以我们可以增加属性Symbol.iterator来实现自定义迭代器,该迭代器功能是输出10个1-10的随机数,由代码结果可以看到,实现iterator接口后,for...of会自动提取出迭代器来遍历。

class RandomValue{
     constructor(number,max){
         this.number = number;
         this.max = max;
     }
     [Symbol.iterator](){
         let count = 1;
         let number = this.number;
         let max = this.max;
         return {
             next(){
                 if(count<=number){
                     count++;
                     return {done:false,val
                 }
                 else{
                     return {done:true,valu
                 }  
             }
         }
     }
 };
 let a7 = new RandomValue(10,10);
 let it4 = a7[Symbol.iterator]();
 console.log(it4.next().value);
 let a8 = new Array();
 for(let i of a7){
     a8.push(i);          
 }
 console.log(a8.toString());
 /*输出结果为:
 10
 7,5,9,4,6,2,7,1,4,1
 5,10,6,4,1,9,7,5,3,8
 */

3.迭代器对象的中途退出现象

迭代器对象中除了next()方法,还有一个可选的return()方法,在迭代器未遍历完全退出时就会调用此函数(俗称关闭迭代器)。

如for-of循环通过break、continue、return或throw提前退出和解构操作并未消费所有值。return()方法也必须要返回一个IteratorResult对象,也可以只返回{done: true }。对象自定义迭代器可以在next()方法下再声明return()方法。

内置的数组类型定义了return()方法后,关闭迭代器时会调用,而且再次调用迭代器时,又会接着上次的游标位置接着遍历。参考代码如下:

let a9 = [1,2,3,4];
let it5 = a9[Symbol.iterator]();
it5.return = function(){
    console.log("数组遍历中途关闭");
    return {done:true}
}
for(let i of it5){
    if(i > 2)
        break;
    console.log(i);
}
for(let i of it5){
    console.log(i);
}
/*
输出结果为:
1
2
数组遍历中途关闭
4
*/