迭代器 Iterator 和生成器 Generator
- 循环:语言层面上的语法 => 重复执行一段程序的方案
- 遍历:业务层面上的做法 => 观察或获取集合中的元素的做法
- 迭代:实现层面上的概念 => 实现遍历的底层方案就是迭代
📖 迭代器 Iterator
1、概念
JavaScript 原有的表示“集合”的数据结构,主要是数组
Array
和对象Object
,ES6 又添加了Map
和Set
。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map
,Map
的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。遍历器 Iterator 就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:
- 一是为各种数据结构,提供一个统一的、简便的访问接口;
- 二是使得数据结构的成员能够按某种次序排列;
- 三是 ES6 创造了一种新的遍历命令
for...of
循环,Iterator 接口主要供for...of
消费。(当使用for...of
循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。)
2、默认的 Iterator 接口
ES6 规定,默认的 Iterator 接口部署在数据结构的
Symbol.iterator
属性,或者说,一个数据结构只要具有Symbol.iterator
属性,就可以认为它是“可遍历的”(iterable)。
ES6 的有些数据结构原生具备 Iterator 接口(比如数组),即不用任何处理,就可以被 for...of
循环遍历。原生具备 Iterator 接口的数据结构如下(Object
并不具备):
Array
String
Map
Set
TypedArray
arguments
- DOM
NodeList
注意:具备 Iterator 接口 => 可以被
for...of
循环遍历
3、迭代器的遍历过程
-
创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
-
第一次调用指针对象的
next
方法,可以将指针指向数据结构的第一个成员。 -
第二次调用指针对象的
next
方法,指针就指向数据结构的第二个成员。 -
不断调用指针对象的
next
方法,直到它指向数据结构的结束位置。
4、会调用 Iterator 接口的场景
一些场合会默认调用 Iterator 接口(即 Symbol.iterator
方法)
for...of
- 解构赋值
- 扩展运算符
...
yield*
- 其他:由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。
Array.from()
Map()
、Set()
、WeakMap()
、WeakSet()
(比如new Map([['a',1],['b',2]])
)Promise.all()
Promise.race()
📖 生成器 Generator
1、概念
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
2、语法
形式上:
function
关键字与函数名之间有一个*
;- 函数体内部使用
yield
表达式,定义不同的内部状态*(yield[jild] 产出)*。
并且对于 *
号的位置并没有要求
function * generator (x, y) { ··· }
function *generator (x, y) { ··· }
function* generator (x, y) { ··· }
function*generator (x, y) { ··· }
3、基本使用
function * generator () {
yield 1;
yield 2;
return 3;
}
const ge = generator();
👉 调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是一个迭代器。
下一步,必须调用遍历器对象的 next()
方法,使得指针移向下一个状态。
而每次调用 next()
方法时,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个 yield
表达式(或 return
语句)为止。
ge.next(); // { value: 1, done: false }
ge.next(); // { value: 2, done: false }
ge.next(); // { value: 3, done: false }
ge.next(); // { value: undefined, done: true }
👉 Generator 函数是分段执行的,yield
表达式是暂停执行的标记,而 next()
方法可以恢复执行。
📑 引用
笔记来自: