ES7 之 Iterator 和 Generator 迭代器详解

迭代器是 JavaScript 中很常见且十分有用的一种设计模式,它可以通过迭代器接口依次访问某种集合中的所有元素。ES7 引入了 Iterator 和 Generator 两个概念,它们可以非常方便地实现迭代器模式,并且使得代码更加清晰、易读。本文将介绍详细的 Iterator 和 Generator 迭代器的概念和用法,希望能给大家带来帮助。

Iterator 迭代器

Iterator 迭代器是一种机制,它可以让用户定义自己的迭代方式,遍历集合中每一个元素。目前,JavaScript 内置支持迭代器的数据类型有 Array、Map、Set、String 和 TypedArray 等。Iterator 迭代器的本质是通过一个 next 方法,控制遍历的过程,同时返回遍历过程中每一个元素的值。迭代器的接口规范为:

interface IteratorResult {
  done: boolean;
  value: any;
}
interface Iterator {
  next(): IteratorResult;
}

其中,done 属性用于判断遍历是否结束,value 属性表示当前取出的值。下面是一个简单的例子,用于演示如何使用迭代器来遍历数组:

let arr = [1, 2, 3];
let iter = arr[Symbol.iterator](); // 通过 Symbol.iterator 得到迭代器
console.log(iter.next()); // { done: false, value: 1 }
console.log(iter.next()); // { done: false, value: 2 }
console.log(iter.next()); // { done: false, value: 3 }
console.log(iter.next()); // { done: true, value: undefined }

可以看到,迭代器的 next 方法返回的是一个迭代结果对象,其中 done 表示遍历是否结束,value 表示当前取出的值。如果迭代结束,则返回的结果对象中 done 属性为 truevalueundefined

使用 for...of 语句,可以方便地使用迭代器遍历集合:

let arr = [1, 2, 3];
for (let v of arr) {
  console.log(v);  // 1, 2, 3
}

由于数组继承自 Array.prototype,而 Array.prototype 本身就实现了迭代器接口,所以上面的代码可以通过数组的迭代器实现。同样,Map、Set、String 等数据类型也都支持迭代器接口。

用户也可以自己定义自己的数据类型实现迭代器接口,从而可以使用迭代器来遍历数据类型。下面是一个示例代码,定义了一个自己的迭代器:

class RangeIterable {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }
  [Symbol.iterator]() {
    let currentValue = this.start;
    let self = this;
    return {
      next() {
        if (currentValue <= self.end) {
          return { done: false, value: currentValue++ };
        } else {
          return { done: true, value: undefined };
        }
      }
    }
  }
}

这个 RangeIterable 类的实例可以用于生成一段连续的整数序列。它通过重载 Symbol.iterator 方法,返回一个可以遍历整数序列的迭代器。

let iterable = new RangeIterable(1, 5);
for (let i of iterable) {
  console.log(i);  // 1, 2, 3, 4, 5
}

Generator 迭代器

Generator 迭代器是 ES6 中引入的一种新的函数类型,它可以看作是一个状态机,可以在执行过程中暂停和恢复执行。在更加底层的层面上,每次调用 Generator 函数时,都会返回一个新的 Iterator 迭代器对象。

使用 Generator 迭代器的语法,需要使用 function* 关键字声明一个 Generator 函数。Generator 函数内部使用 yield 关键字可以控制函数的执行过程:

function* myGenerator() {
  yield 1;
  yield 2;
  yield 3;
  return 4;
}
let iter = myGenerator();
console.log(iter.next());  // { done: false, value: 1 }
console.log(iter.next());  // { done: false, value: 2 }
console.log(iter.next());  // { done: false, value: 3 }
console.log(iter.next());  // { done: true, value: 4 }

在 Generator 函数内部,我们可以使用 yield 关键字来暂停函数的执行过程,以便用户可以通过 next 方法来控制函数的执行过程。同时我们还可以在 Generator 函数内部使用 return 语句来终止迭代器的遍历。

Generator 迭代器的最大的优点在于,它可以非常便捷地实现从异步操作中返回数据。通过使用 yield 关键字,我们可以将异步操作转换为同步操作。下面是一个简单的例子,用于演示如何使用 Generator 迭代器来实现异步操作:

function asyncFun() {
  setTimeout(() => generator.next('hello'), 1000);
}
function* myGenerator() {
  const result = yield asyncFun();
  console.log(result);  // hello
}
let generator = myGenerator();
generator.next();

用于实现异步操作的 asyncFun 函数,利用了 setTimeout 函数,延迟了执行时间。而 myGenerator 函数中使用了 yield 关键字来暂停函数的执行过程。在异步操作完成后,再次调用 generator.next 方法,继续执行函数。

总结

Iterator 和 Generator 迭代器是 JavaScript 中非常有用的机制,它们可以方便地实现迭代器模式,帮助我们遍历集合中的元素。同时,对于异步操作等情况,使用 Generator 迭代器可以实现非常完美的效果。希望本文能够给大家带来帮助。

参考资料

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/659e2a42add4f0e0ff73abdc


纠错反馈