【译】ES6中的 Iterators

【注】其实吸引个人是这张配图。。。让我想起了卷福划的圈,有没有~~javascript

咱们将在文章中分析一下Iterators(迭代器)。Iterators是JS中循环集合的一种新的方式。它是ES6中引入的,已经在不少场景中被普遍使用,所以已经变得很是受欢迎。html

简介

若是你有这样一个数组:java

const myFavouriteAuthors = [
  'Neal Stephenson',
  'Arthur Clarke',
  'Isaac Asimov', 
  'Robert Heinlein'
];
复制代码

有时候,你想要获取数组里的每一个值,用来打印,操做,或者作一些其它逻辑。若是我问你怎么作?你确定会说 —— 这很简单。“用forwhile,for-of就好了”。实现将会是这样:es6

for,while,for-of

设想一下,若是你须要自定义一下刚刚那个数组结构,好比保存做者信息,像这样:数组

Custom Data Structure

如今,myFavouriteAuthors是一个对象了,包含了另外一个对象allAuthorsallAuthors又包含了3个数组,fictionscienceFiction以及fantasy。如今,若是我再问你如何经过循环myFavouriteAuthors来获取全部做者,那你会怎么来实现呢?你能够继续尝试用复合循环来获取全部数据。数据结构

不过,若是你这样作:app

for (let author of myFavouriteAuthors) { 
  console.log(author)
}
// TypeError: {} is not iterable
复制代码

你将会获得TypeError说object不能被迭代。让咱们看下可迭代是什么意思,而且咱们如何才能让这个object能够迭代。在文章最后,你将会知道如何用for-of去迭代这个myFavouriteAuthorside

**Iterables(迭代) and Iterators(迭代器)函数

上一节咱们遇到了个问题。在咱们自定义的对象里获取全部做者信息不是那么的方便。咱们须要某种方法,可以把全部(对象)内数据有顺序的暴露出来。ui

让咱们在myFavouriteAuthors里加一个方法getAllAuthors,用来返回全部做者,像这样:

getAllAuthors

这是个简单的实现。它完成了咱们的任务。然而,这样会出现一些问题:

  • getAllAuthors这个命名太自由了。若是有些人有一个他们本身的myFavouriteAuthors,他们可能会取名叫retrieveAllAuthors
  • 做为一名开发人员,咱们就总要去找这个用来获取全部数据的方法名字。固然在这里,它叫getAllAuthors
  • getAllAuthors 返回的是包含全部做者字符串的数组。但若是另一个开发人员返回了一个对象组成的数组,怎么办呢?

[ {name: 'Agatha Christie'}, {name: 'J. K. Rowling'}, ... ]

那么开发人员必需要知道这个方法的准确名字以及返回的数据格式。

若是咱们定一个规则,方法的名字和返回类型是固定不可变的呢?

让咱们把这个方法叫作 —— iteratorMethod

ECMA采起了相似的步骤来标准化这个循环自定义对象的过程。然而,ECMA用名为Symbol.iterator来代替了iteratorMethod名字。Symbols提供了一个独一无二的名字(惟一,而且不会和别的属性名发生冲突)。Symbol.iterator将会返回一个叫作iterator的对象。这个iterator有一个next的方法,这个方法会返回一个用valuedone做为key的对象。

这个叫value的key将包含当前值。它是任何类型的。这个done是一个布尔值。它表示是否已经获取到了全部的值。

用一张图来帮助说明一下 iterables, iterators , next 之间的关系。而 这个关系被称为 Iteration Protocol(迭代协议)

Relationship between iterables, iterators, and next.

按照Axel Rauschmayer的Exploring JS这本书所说:

  • 迭代是一种数据结构,能公开它的元素可访问性。这个Symbol.iterator就是用来作这件事的方法。这个方法是一个 迭代器 工厂,也就是说,它将会建立一个 迭代器
  • iterator(迭代器) 是指向用来遍历元素数据集的指针。

** 让 objects 变得可迭代

咱们已经从上一节了解到,咱们须要实现一个叫作 Symbol.iterator 的方法。咱们将会使用computed property syntax(可计算的属性语法)来设置这个key。示例以下:

Example of iterable

第四行,咱们定义了一个迭代器。是一个带有next方法的对象。这个next方法返回的值取决于step变量。在25行,咱们获取了这个iterator。27行,咱们调用next。咱们调用next屡次,直到done变成true

这正是在for-of循环里发生的事情。for-of拿到这个对象的迭代器,而后循环调用next()直到done变成true。

JavaScript中的迭代

JS里有不少可迭代的东西。可能不是显而易见的,可是你仔细检查一下,迭代器就会出来了。

这些是可迭代的:

  • Arrays and TypedArrays
  • Strings —— 能迭代出每一个字符或者Unicode码
  • Maps —— 迭代出key-value对
  • Sets —— 迭代出元素
  • arguments —— 相似array
  • DOM元素 —— 正在开发中。。。

这些是会使用迭代器的:

  • for-of循环 —— 须要对象可迭代,不然会抛出一个TypeError

for (const value of iterable) { ... }

  • 数组的解构 —— 由于可迭代而发生了解构。让咱们看下:
const array = ['a', 'b', 'c', 'd', 'e'];
const [first, ,third, ,last] = array;
复制代码

这样就等同于:

const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const first = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const third = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const last = iterator.next().value
复制代码
  • ...操做符

代码以下:

const array = ['a', 'b', 'c', 'd', 'e'];
const newArray = [1, ...array, 2, 3];
复制代码

等同于:

const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const newArray = [1];
for (let nextValue = iterator.next(); nextValue.done !== true; nextValue = iterator.next()) {
  newArray.push(nextValue.value);
}
newArray.push(2)
newArray.push(3)
复制代码
  • Maps and Sets

Map的构造函数会迭代出[key,value]放进Map里,Set的构造函数会迭代出元素放进Set里:

const map = new Map([[1, 'one'], [2, 'two']]);
map.get(1) 
// one
const set = new Set(['a', 'b', 'c]);
set.has('c');
// true
复制代码
  • 迭代器也是理解generator函数的前提

myFavouriteAuthors能够迭代

直接上代码:

const myFavouriteAuthors = {
  allAuthors: {
    fiction: [
      'Agatha Christie', 
      'J. K. Rowling',
      'Dr. Seuss'
    ],
    scienceFiction: [
      'Neal Stephenson',
      'Arthur Clarke',
      'Isaac Asimov', 
      'Robert Heinlein'
    ],
    fantasy: [
      'J. R. R. Tolkien',
      'J. K. Rowling',
      'Terry Pratchett'
    ],
  },
  [Symbol.iterator]() {
    // Get all the genre in an array
    const genres = Object.values(this.allAuthors);
    
    // Store the current genre and author index
    let currentAuthorIndex = 0;
    let currentGenreIndex = 0;
    
    return {
      // Implementation of next()
      next() {
        // authors according to current genre index
        const authors = genres[currentGenreIndex];
        
        // doNotHaveMoreAuthors is true when the authors array is exhausted.
        // That is, all items are consumed.
        const doNothaveMoreAuthors = !(currentAuthorIndex < authors.length);
        if (doNothaveMoreAuthors) {
          // When that happens, we move the genre index to the next genre
          currentGenreIndex++;
          // and reset the author index to 0 again to get new set of authors
          currentAuthorIndex = 0;
        }
        
        // if all genres are over, then we need tell the iterator that we 
        // can not give more values.
        const doNotHaveMoreGenres = !(currentGenreIndex < genres.length);
        if (doNotHaveMoreGenres) {
          // Hence, we return done as true.
          return {
            value: undefined,
            done: true
          };
        }
        
        // if everything is correct, return the author from the 
        // current genre and incerement the currentAuthorindex
        // so next time, the next author can be returned.
        return {
          value: genres[currentGenreIndex][currentAuthorIndex++],
          done: false
        }
      }
    };
复制代码

引用:

原文

相关文章
相关标签/搜索