迭代协议与生成器 101

Intro

首先关于这些名称可能不少人都很模糊不清,尽管都是有关迭代遍历但并不清楚彼此的定义和区别,那如今咱们就慢慢的解释清楚。数组

terminology

那咱们优先从每个术语称呼开始吧,来看看这些术语对应的中文名:bash

  • iterable object: [可]迭代对象异步

  • Iteration protocols: 迭代协议async

  • generator:生成器函数

Iteration protocols - 迭代协议

细心的同窗大概已经发现 Iteration protocols 是复数。是的在协议里其实有2种协议分别是:工具

  • Iteration protocolsui

    • iterator protocol:迭代器协议this

    • iterable protocol:可迭代协议编码

what are they?

在以往的JavaScripts中已经存在许多对集合类型对迭代方法,例如:for .. in, for 循环, map(), forEach()... 这些语法或者API都是有JavaScript内部实现如何进行迭代集合。例如:spa

  1. for .. in语法就是遍历全部non-Symbol的可枚举属性
  2. map()API 就是对数组对索引依次遍历获得一个新数组;

那么咱们如何对一个变量实现咱们自定义的迭代方式呢,这就须要依靠迭代协议。其实在我看来 迭代器协议 与 可迭代协议 是很是相似的以致于初探时后很难弄清,下面咱们分别来看看2个协议。

iterator protocol - 迭代器协议

image
😰😰😰

其实 迭代器协议 就是一个对象上定义了一个next属性,而这个next属性的定义有必定的要求,知足的话这就是一个实现了迭代器协议的对象,也被叫作 iterator : 迭代器

那么这个next属性有声明要求呢?

  1. 首先 next 是一个方法,它不接受任何参数传入;
  2. 其次调用next这个方法会返回一个对象,它包含2个属性 value & done,其中 value表明本次迭代获得的数据而 done用来表示迭代是否结束。例如:
    image
    以上 iterator 变量就是一个实现了迭代器协议的迭代器对象。

✌️PS:迭代器协议只是一种协议它指定了一个对象的迭代行为控制(每次迭代返回值、迭代终点),但如何自动迭代运行还须要本身编码实现(否则你得彻底编码不停的iterator.next()、iterator.next()、iterator.next()),且每次迭代的上下文你得本身想办法保留。

iterable protocol - 可迭代协议

image
🤬🤬🤬

其实可迭代协议是一个对象是拥有 @@iterator 属性,而这个属性键的定义来自 Symbol.iterator, 一样@@iterator 属性有必定要求,知足要求就实现了可迭代协议。

这些要求分别是:

  1. [Symbol.iterator](key name)属性是一个方法,且不接受任何参数;
  2. 方法返回一个对象,这个对象就是迭代器协议对象。例如:
    image

这就是两种迭代协议对内容与区别,那么说完迭代协议咱们能够来谈谈iterable object。

iterable object - [可]迭代对象

可迭代对象是对象上实现了 iterable protocol - 可迭代协议 的对象,且可使用build-ins语法进行迭代,例如 for (let i in iterable)[...iterable]。 ⚠️注意: 使用这些build-ins语法必须是对象上实现了可迭代协议不是迭代器协议,不然对对象迭代将会抛出异常:

❌ Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
    at <anonymous>:1:1
复制代码

目前有不少JavaScript内置对数据集合已经实现了迭代器协议,有:

  1. Array
const iterable = [10, 20, 30];

for (const value of iterable) {
  console.log(value); // 10 20 30
}
复制代码
  1. String
const iterable = 'boo';

for (const value of iterable) {
  console.log(value);  // 'b' 'o' 'o'
}
复制代码
  1. TypedArray
  2. Map & Set
  3. fn arguments
  4. DOM collections

到此你可能发现了要实现自定义迭代行为在写法上仍是很复杂对,而且这里存在两种迭代协议,不一样的库或者工具可能选用某一种方法实现迭代对象的行为,那么就会可能形成不兼容。但因为2中协议期本质又是十分相似全部咱们能够创造一个同时知足迭代器协议和可迭代协议的对象,它相似:

var myIterator = {
    next: function() {
        // ...
    },
    [Symbol.iterator]: function() { return this }
}
复制代码

这样看起来仍是很复杂,因而有了咱们最后要说的 generator

generator - 生成器

generator对象generator函数 返回,它既符合[可]迭代协议,又符合迭代器协议,就像刚刚那种模版写法。

它的写法以下:

// 生成器函数
function* gen() { 
  yield 1;
  yield 2;
}

// 生成器对象
const g = gen();
复制代码

JavaScript支持了生成器语法咱们就能够更快的实现自定义的迭代对象了,例如上面的一个例子我用生成器实现是这样的:

image

具体的 generator 语法再次再也不过多解释,这就是 generator 与 itera... 之间的关系。

⚠️注意: 生成器对象不要重复使用 这句话什么意思,咱们先来看一个MDN例子🌰:

const gen = (function *(){
  yield 1;
  yield 2;
  yield 3;
})();
for (const o of gen) {
  console.log(o);
  break;  // Closes iterator
}

// The generator should not be re-used, the following does not make sense!
for (const o of gen) {
  console.log(o); // Never called.
}
复制代码

以上代码咱们能够知道 generator对象 就像是一个一次性消费品(一次性筷子🥢)被迭代行为操做一次后将不会再次进行迭代。


基本上全部的东西就说完了,在补充说明最后一点东东

  1. 有了自定义迭代那么如何实现 迭代流 实现 非阻塞代码呢?在很早之前TJ大佬有实现一个库CO就是干这件事情的该库在社区也比较流行。
  2. 迭代器是一个很好去写异步代码的方式,但在 es2017 async/await 语法糖的引入,是的异步代码的编写与阅读更加方便。
相关文章
相关标签/搜索