ES6的生成器和迭代器

ES6的生成器和迭代器
ES6为JavaScript语言带来了许多新特性。其中两个特性,生成器和迭代器,极大地改变了我们在更复杂的前端代码中编写特定函数的方式。
虽然他们之间的关系很好,但他们实际上做的事情可能有点令人困惑,所以让我们来看看他们。

迭代器

迭代在编程中是一种常见的做法,通常用于循环一组值,要么转换每个值,要么使用或以某种方式保存它。
在JavaScript中,我们总是有这样的for循环:

for (var i = 0; i < foo.length; i++){
  // do something with i
}

但是ES6给了我们一个选择:

for (const i of foo) {
  // do something with i
}

这可以说是更清洁、更容易使用,并让我想起了Python和Ruby之类的语言。但是还有一件事对于这种新的迭代非常重要:它允许您直接与数据集的元素交互。
假设我们想知道数组中的每个数是否是质数。我们可以用一个函数来做这个。它可能是这样的:

function isPrime(number) {
  if (number < 2) {
    return false;
  } else if (number === 2) {
    return true;
  }
  for (var i = 2; i < number; i++) {
    if (number % i === 0) {
      return false;
      break;
    }
  }
  return true;
}

不是世界上最好的,但它能起作用。下一步是对我们的数字列表进行循环,并检查是否每一个都是我们的新函数。这是很简单的:

var possiblePrimes = [73, 6, 90, 19, 15];
var confirmedPrimes = [];
for (var i = 0; i < possiblePrimes.length; i++) {
  if (isPrime(possiblePrimes[i])) {
    confirmedPrimes.push(possiblePrimes[i]);
  }
}
// confirmedPrimes is now [73, 19]

它仍然有效,但是它很笨重,而且笨拙很大程度上取决于JavaScript处理循环的方式。但是,使用ES6,我们在新迭代器中得到了一个几乎是python的选项。所以之前的for循环可以写成这样:

const possiblePrimes = [73, 6, 90, 19, 15];
const confirmedPrimes = [];
for (const i of possiblePrimes){
   if ( isPrime(i) ){
      confirmedPrimes.push(i);
   }
}
// confirmedPrimes is now [73, 19]

这是非常干净的,但最显著的一点是for循环。变量i现在表示的是数组中的实际项目,称为possiblePrimes。所以我们不再需要用指数来表示。这意味着我们可以调用i,而不是在循环中调用possiblePrimes[i]。
在幕后,这种迭代利用了ES6的Symbol.iterator()方法。这个方法负责描述迭代,当调用时,返回一个JavaScript对象,该对象包含循环中的下一个值,以及一个已完成的键,这取决于循环是否完成,这要么是正确的,要么是错误的。

生成器

生成器也称为“迭代器工厂”,是一种创建特定迭代的新型JavaScript函数。他们给你特殊的自定义方式来循环。
好吧,那是什么意思?让我们来看一个例子。假设我们想要一个函数每次调用它时它都会给我们下一个质数。同样,我们将使用isPrime函数来检查一个数字是否为质数:

function* getNextPrime() {
  let nextNumber = 2;
  while (true) {
    if (isPrime(nextNumber)) {
      yield nextNumber;
    }
    nextNumber++;
  }
}

如果你习惯了JavaScript,有些东西看起来有点像巫术,但实际上还不算太糟。我们在关键字函数后面有一个奇怪的星号,但是所有这些都是为了告诉JavaScript我们定义了一个生成器。
另一个有趣的部分是yield关键字。这就是当你调用生成器时,它会吐出的东西。它大致等价于返回,但它保留了函数的状态,而不是在调用时重新运行任何东西。它会在运行时“记住”它的位置,所以下次你调用它时,它会继续它的位置。
这意味着我们可以这样做:

const nextPrime = getNextPrime();

然后在我们想要得到的时候调用nextPrime——你猜到了——下一个质数:

console.log(nextPrime.next().value); // 2
console.log(nextPrime.next().value); // 3
console.log(nextPrime.next().value); // 5
console.log(nextPrime.next().value); // 7

你也可以直接调用,这在你的发生器不是无限的情况下很有用,因为它返回一个像这样的对象:nextPrime.next()

console.log(nextPrime.next());
// {value: 2, done: false}

在这里,done键告诉您函数是否完成了它的任务。在我们的例子中,我们的函数永远都不会完成,理论上我们可以把所有质数都加到无穷大(当然,如果我们有这么多计算机内存的话)。

很酷,我现在可以使用生成器和迭代器吗?

虽然ECMAScript 2015已经最终确定并且已经有好几年了,但浏览器对其功能特别是发生器的支持还远远没有完成。如果您真的想要使用这些和其他现代功能,您可以查看像Babel和Traceur这样的转译器,它可以将您的ECMAScript 2015代码转换为ECMAScript 5代码(如果可能)。
也有许多在线编辑支持ECMAScript 2015,或者专门关注它,特别是Facebook的Regenerator和JS Bin。如果您只是想找点东西来玩游戏,想想如何编写JavaScript,那么值得一看。

结论

在我们的JavaScript问题方法中,IGenerators和迭代器为我们提供了很多新的灵活性。迭代器允许我们使用Pythonic编写for循环的方式,这意味着我们的代码看起来更干净,更易于阅读。
生成器函数使我们能够编写函数,记住上次看到它们时的位置,并可以从停止的位置获取它们。就实际记忆的数量而言,它们也可以是无限的,在某些情况下它们可以非常方便地使用。
支持这些生成器和迭代器是很好的。它们受Node和所有现代浏览器支持,但Internet Explorer除外。如果您需要支持旧浏览器,最好的办法就是使用Babel等转换器。

原创文章,作者:webstack,如若转载,请注明出处:https://www.webstacks.cn/tutorial/1113.html

发表评论

登录后才能评论