Skip to content

Files

Latest commit

740fb0a · Jul 6, 2019

History

History
229 lines (159 loc) · 6.36 KB

34.md

File metadata and controls

229 lines (159 loc) · 6.36 KB

27 同步迭代

原文: http://exploringjs.com/impatient-js/ch_sync-iteration.html

27.1 什么是同步迭代?

同步迭代是一个 协议(接口加上使用它们的规则),它连接 JavaScript 中的两组实体:

  • 数据来源: 一方面,数据有各种形式和大小。在 JavaScript 的标准库中,有线性数据结构 Array,有序集合 Set(元素按添加时间排序),有序字典 Map(条目按添加时间排序)等等。在库中,您还可以找到树形数据结构等。

  • 数据消费者: 另一方面,你有一整套机制和算法,只需要按顺序访问:一次一个,再加上告诉我什么时候完成的方法。示例包括for-of循环并扩展到数组字面值(通过...)。

迭代协议通过接口Iterable连接这两部分:数据源按顺序“穿过它”传递它们的内容;数据消费者通过它获得输入。

Figure 18: Data consumers such as the for-of loop use the interface Iterable. Data sources such as Arrays implement that interface.

Figure 18: Data consumers such as the for-of loop use the interface Iterable. Data sources such as Arrays implement that interface.

图中的图表 18 说明了迭代的工作原理:数据消费者使用接口Iterable;数据源实现它。

JavaScript 实现接口的方式

在 JavaScript 中,如果一个对象具有接口声明的所有方法,即为 实现 该接口。本章中提到的接口仅存在于ECMAScript规范中。

数据的来源和消费者都从这种安排中获益:

  • 如果您开发新的数据结构,则只需要实现Iterable,就可以立即应用大量工具。

  • 如果编写使用迭代的代码,它会自动使用许多数据源。

27.2 核心迭代构造:迭代接口和迭代器

两个角色(由接口描述)构成了迭代的核心(图 19 ):

  • iterable 接口是一个对象,其内容可以顺序遍历。
  • 迭代器 是用于遍历的指针。

Figure 19: Iteration has two main interfaces: Iterable and Iterator. The former has a method that returns the latter.

Figure 19: 迭代有两个主要的接口: IterableIterator。前者有一种返回后者的方法。

这些是迭代协议接口的类型定义(以 TypeScript 的表示法):

interface Iterable<T> {
  [Symbol.iterator]() : Iterator<T>;
}

interface Iterator<T> {
  next() : IteratorResult<T>;
}

interface IteratorResult<T> {
  value: T;
  done: boolean;
}

接口使用如下:

  • 您可以通过键为 Symbol.iterator 的方法向 Iterable 接口请求迭代器。
  • Iterator 通过其方法 .next() 返回迭代值。
  • 这些值不会直接返回,而是包含在具有两个属性的对象中:
    • .value 是迭代值。
    • .done 表示是否已达到迭代结束。在最后一次迭代值和 false 之后是true

27.3 手动迭代

这是使用迭代协议的示例:

const iterable = ['a', 'b'];

// The iterable is a factory for iterators:
const iterator = iterable[Symbol.iterator]();

// Call .next() until .done is true:
assert.deepEqual(
  iterator.next(), { value: 'a', done: false });
assert.deepEqual(
  iterator.next(), { value: 'b', done: false });
assert.deepEqual(
  iterator.next(), { value: undefined, done: true });

27.3.1 通过while迭代一个迭代(Iterable)

以下代码演示了如何使用while循环迭代一个迭代:

function logAll(iterable) {
  const iterator = iterable[Symbol.iterator]();
  while (true) {
    const {value, done} = iterator.next();
    if (done) break;
    console.log(value);
  }
}

logAll(['a', 'b']);
// Output:
// 'a'
// 'b'

练习:手动使用同步迭代

exercises/sync-iteration-use/sync_iteration_manually_exrc.mjs

27.4 实践中的迭代

我们已经看到了如何手动使用迭代协议,这是相对麻烦的。但该协议并不是直接使用的——它旨在通过构建在其上的更高级语言结构来使用。本节介绍了它看起来是什么样的。

27.4.1 迭代数组(Array)

JavaScript 的数组是可迭代的。这使我们可以使用for-of循环:

const myArray = ['a', 'b', 'c'];

for (const x of myArray) {
  console.log(x);
}
// Output:
// 'a'
// 'b'
// 'c'

通过数组模式进行解构(稍后解释)也使用了迭代:

const [first, second] = myArray;
assert.equal(first, 'a');
assert.equal(second, 'b');

27.4.2 迭代 Set

JavaScript 的 Set 数据结构是可迭代的。这意味着,for-of有效:

const mySet = new Set().add('a').add('b').add('c');

for (const x of mySet) {
  console.log(x);
}
// Output:
// 'a'
// 'b'
// 'c'

和数组解构一样:

const [first, second] = mySet;
assert.equal(first, 'a');
assert.equal(second, 'b');

27.5 快速参考:同步迭代

27.5.1 可迭代数据源

以下内置数据源是可迭代的:

  • Arrays
  • Strings
  • Maps
  • Sets
  • (浏览器:DOM 数据结构)

要迭代对象的属性,你需要一些“帮手”,诸如 Object.keys()Object.entries() 。这很必要,因为属性存在于与数据结构级别互补的不同级别。

27.5.2 迭代结构

以下构造都基于迭代:

  • 通过数组模式进行解构:

    const [x,y] = iterable;
  • for-of循环:

    for (const x of iterable) { /*···*/ }
  • Array.from()

    const arr = Array.from(iterable);
  • 将(通过...)传播到数组和函数调用中:

    const arr = [...iterable];
    func(...iterable);
  • new Map()new Set()

    const m = new Map(iterableOverKeyValuePairs);
    const s = new Set(iterableOverElements);
  • Promise.all()Promise.race()

    const promise1 = Promise.all(iterableOverPromises);
    const promise2 = Promise.race(iterableOverPromises);
  • yield*

    function* generatorFunction() {
      yield* iterable;
    }

测验

参见测验应用程序