JavaScript 生成器之精髓

发布于 2024-10-14 08:05:47 字数 3289 浏览 9 评论 0

迭代器/生成器是一组用以做自动化流程控制的语法对象。其核心价值点有二: 可自动化的流程设计与控制可暂停特性 。其他所谓价值都是纸老虎。

迭代器 - iterator

迭代器和生成器是你天天在用但又感知不到的东西。生成器是用来生成迭代器的东西,其基础还是迭代器。让我们先从迭代器入手。

迭代器是一类 特殊对象 ,它具有用来 访问迭代过程专用接口 。迭代者,循环也。可以这样理解,它是专门用来处理循环过程的一类接口,最终达到简化循环的目的。任何具备这类接口特征的对象,都认为具有了迭代器的特征。先来看看手写循环过程的痛点在哪里:

const colors = ['blue', 'green', 'red']
for (let i = 0; i < colors.length; i++) {
  doSomething(colors[i])
}

这可能是你每天都会写的代码。但看完这篇文章后,希望这样的代码不要再出现于你的手下。2018 年,不应该再用这么命令式的方式写代码了。命令式有什么毛病呢?

  1. 对于每类数组,其循环结构的模板代码都是重复的。代码量较大,重复多,不优雅
  2. 需要手动管理索引,以获得对应元素的值
  3. 对于多重循环,不同层之间的索引值可能相互引用修改,难以调试

相反,我们至少应该以更加「声明式」一些的思路来写。比如,以下的方式:

for (color of colors) {
  doSomething(color)
}

这是 ES6 提供的 for..of 语法。当然,你可以说它就是把循环过程封装在关键字下,但试想一下,如果它处理的不是数组呢?你就不能以索引的方式来访问元素了,对不?你也就不能用 length 的方式来判断循环结束了,对不?于是我们想到,为了使我们对命令式循环的处理流程更加通用,我们需要为每一类所谓的「可迭代对象」编写一个具有统一接口的迭代器,然后就能在 for..of 底下基于一套相同的迭代器接口来访问它们了。

一个典型的迭代器,主要就是要提供一个 next 接口,这个接口需要回答两个问题:下一个元素的值是啥,以及是否还有下一个元素。

function createArrayIterator(items) {
  let index = 0
  return {
    next() {
      const done = index >= items.length
      const value = done ? undefined : items[index++]

      return { value, done }
    },
  }
}

在 ES6 中,这个迭代器函数被保存到原型上的 [Symbol.iterator] 键上。 也即是说,所有提供了这个键的对象,都能被当成可迭代对象被访问 (如 for..of 运算符、展开运算符 ...object 等)。JS 中内建类型的可迭代对象分别有:

内建类型迭代器
Array.prototype[Symbol.iterator][Function: values]
Set.prototype[Symbol.iterator][Function: values]
Map.prototype[Symbol.iterator][Function: entries]
String.prototype[Symbol.iterator][Function: [Symbol.iterator]]

然而呢,到目前为止,这个东西看起来还没什么价值。除了,它本身是一种十分常见的编程模式;除了,可以为你自定义的对象定义迭代器……好像就没啥价值了。

生成器 - generator

生成器函数。生成什么的函数呢?是生成迭代器的一个函数,它会返回一个迭代器,只不过用它生成的迭代器,除了可以 next() ,还可以 throw() 。一方面,可以用它来方便地生成一些迭代器;但它最大的价值,还在于 yield 语句提供了暂停执行的能力。只有在被 next() 的时候,它才会接着往下执行。

这里有两个重点:一个是,被谁 next() ?一个是,这种暂停执行的能力有什么价值?

不管是被谁 next() ,它都应该是一个 自动化的程序 。抽象出迭代器接口,以及自动化创建迭代器的生成器函数,目标无非是为了更灵活、更自动化的流程控制。因此,只有当这个「流程控制」具备自动化的能力时,生成器和迭代器函数才显出其价值。在语言规范中,称这两者为 Control Abstraction(流程)控制抽象层,也是这个道理。这是学习生成器质问其价值的一个重要回答。也就是说,很多其他(比如知乎上讲的)作用都不是非生成器不可的价值。

其次是,这种暂停执行的能力有什么价值?目前看到的就是实现异步流程的同步化( async / await ),以及 redux-saga 这样的应用。其他还没看到什么特别的价值。有些人说,支持「懒求值」,这个事情我看来好像就是瞎扯,因为如果 generator 函数最终被自动化,你哪里有手动「懒」一把的地方?还不是一把全执行完。当然,可能是我没有理解到它的场景。

总结起来,什么时候可以考虑用生成器技术呢,两个:

  • 需要实现一套 可自动化流程控制 程序时
  • 需要利用 暂停执行 的能力时

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

林空鹿饮溪

暂无简介

0 文章
0 评论
23 人气
更多

推荐作者

qq_z5gHLI

文章 0 评论 0

圈圈圆圆圈圈

文章 0 评论 0

alipaysp_h2Vbo4sv6k

文章 0 评论 0

初见你

文章 0 评论 0

清风无影

文章 0 评论 0

云胡

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文