六、ES6系列之Iterator 和 for...of 循环
介绍
首先定义一个数组:
1 | const person = [ |
在某些时候,您会想要取回数组中的所有单个值,以便将它们打印在屏幕上、操作它们或对它们执行某些操作。如果我问你,你会怎么做?你会说——这很容易。我就对他们循环中使用 for,while,for-of 或一个这些循环的方法。示例实现是:
1 | // For loop |
现在,假设有一个自定义数据结构来保存所有 person,而不是之前的数组。像这样:
1 | const person = { |
现在,person 是一个包含另一个对象的对象 subject。subject 包含三个阵列,按键 english,math 和 chinese。现在,如果要获取 person 所有人员,我们尝试一些循环组合来获取所有数据。
1 | for (const value of person) { |
得到一个 TypeError 说法,该对象不可迭代。那么这是为什么呢,下面我们看看什么是可迭代对象以及如何使对象可迭代。
Iterator(迭代器)
所谓迭代器,其实就是一个具有 next() 方法的对象,每次调用 next() 都会返回一个结果对象,该结果对象有两个属性,value 表示当前的值,done 表示遍历是否结束。
来看我们上一节中的问题,我们想要某种方法,通过它我们可以拿到所有内部数据。
让我们在 person 对象中添加一个方法 getAllPerson,来返回返回所有作者。
1 | const person = { |
现在我们通过一个简单的方法拿到所用人员的名字;但是,这种实现可能会出现一些问题。比如:
- getAllPerson 是一个具体的名字,不同的开发者会有自己的命名习惯,比如 retrieveAllPerson;
- 作为开发人员,我们总是需要知道返回所有数据的特定方法。 在本例中,它被命名为 getAllPerson。
- getAllPerson 返回类型是固定的字符串数组。
开发人员必须知道返回所有数据的方法的确切名称和返回类型。
如果我们制定一个规则,即方法的名称及其返回类型 将是固定的且不可更改的。
我们将此方法命名为 iteratorMethod。
ECMA采取了类似的步骤 来标准化这个循环自定义对象的过程。但是,ECMA 没有使用 iteratorMethod 作为 方法的名称 ,而是使用 Symbol.iterator。Symbol 类型可以提供唯一且不会与其他属性名称冲突的名称。此外,Symbol.iterator 将返回一个迭代器对象。该迭代器将调用一个方法 next ,它将返回一个带有键 value 和 done 的对象。
图表可能有助于理解可迭代对象、迭代器和下一个之间的关系。这种关系称为迭代协议。

在Exploring JS一书中有这样一段介绍:
- 一个迭代是想让它的元素向公众开放的数据结构。它通过实现一个键为 的方法来实现 Symbol.iterator。该方法是迭代器的工厂。也就是说,它将创建迭代器。
- 一个迭代器是用于遍历数据结构的元素的指针。
使 object 可迭代
因此,正如我们在上一节中学到的,我们需要实现一个名为 Symbol.iterator 的方法 。来创建一个迭代器。
1 | function createIterator(items) { |
For-of
除了迭代器之外,我们还需要一个可以遍历迭代器对象的方式,ES6 提供了 for of 语句,我们直接用 for of 遍历一下我们上节生成的遍历器对象试试:
1 | var iterator = createIterator([1, 2, 3]); |
结果报错 TypeError: iterator is not iterable,表明我们生成的 iterator 对象并不是 iterable(可遍历的)。
那什么才是可遍历的呢?
其实一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。
ES6 规定,默认的 Iterator 接口部署在数据结构的 Symbol.iterator 属性,或者说,一个数据结构只要具有 Symbol.iterator 属性,就可以认为是”可遍历的”(iterable)。
举个例子:
1 | const obj = { |
由此,我们也可以发现 for of 遍历的其实是对象的 Symbol.iterator 属性。
默认可遍历对象
很多东西在 JavaScript 中都是可迭代的。这是因为 ES6 默认部署了 Symbol.iterator 属性。
数组和类数组
1 | for (const x of ["a", "b"]) { |
字符串
遍历每个字符或 Unicode 编码
1 | for (const x of "a\uD83D\uDC0A") { |
Maps
遍历其键值对
1 | const map = new Map().set("a", 1).set("b", 2); |
Sets 元素
遍历其键值对
1 | const set = new Set().add("a").add("b"); |
使 person 可迭代
最后我们来看文章开头的示例,使其实现可迭代。
1 | const person = { |
总结
最后总结下,迭代器是可迭代对象,具有 Symbol.iterator 方法和 next()方法,可以通过 for..of 代替普通 for 循环来迭代,省去循环引用变量,简化了循环过程。