JavaScript中 Iterator 是什么

今天我们来学习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 接口/协议

有两种协议接口,分别为iteratoriterable

Iterator 接口/协议

当一个对象实现下面的功能接口时,那它就是一个迭代器

  • 还有剩下的元素吗?
  • 如果有,元素是什么?

从技术上说,当一个对象有一个next()方法并返回下面两个属性时,它就是迭代器

  • done: 一个布尔值,代表是否还有下一个元素,如果有就是true,反之为false
  • value: 当前值

每次调用 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迭代器,以及如何使用迭代接口来实现自定义的迭代逻辑。

如果本文有帮助,微信搜索【小帅的编程笔记】,让我们每天进步

猜你喜欢

转载自blog.csdn.net/cmdfas/article/details/120712686