JavaScript原有的表示“集合”的数据结构,有数组Array
和对象Object
,ES6新添了set
和Map
,这样就有了四种数据集合。可以组合使用,定义自己的数据结构,比如数组的成员是Map
,Map
的成员是对象,这样就需要一个统一的接口机制,来处理所有不同的数据结构。
迭代器Iterator
接口,就可以完成遍历操作(依次处理改数据结构的所有成员)。
Iterator的作用有三个:
- 为各种数据结构提供一个统一的、简便的访问接口。
- 使数据结构的成员能够按照某种次序排列。
- 为遍历指令
for...of
循环服务。
Iterator的遍历过程:
- 创建一个指针对象,指向当前数据结构的起始位置。(也就是说,遍历器对象本身就是一个指针对象)
- 第一次调用指针对象的
next
方法,可以将执政指向数据结构的第一个成员。 - 第二次调用指针对象的
next
方法,指针就指向数据结构的第二个成员。 - 不断调用执政对象的
next
方法,直到他指向数据结构的结束位置。
每一次调用next
方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value
和done
两个属性的对象。其中,value
属性是当前成员的值,don
属性是一个布尔值,表示遍历是否结束。
尝试来模拟下next
返回值的例子:
1 | function makeIterator(array) { |
默认Iterator接口
Iterator接口的目的,就是为所有数据结构,提供一种统一的访问机制:for...of
。使用for...of
循环遍历某种数据结构时,该循环会自动去寻找Iterator
接口。一种数据结构只要部署了Iterator
接口,那么这种数据结构就是“可遍历的”(Iterable)
ES6规定,默认的Iterator
接口部署在数据结构的Symbol.iterator
属性里。或者说,一个数据结构具有了Symbol.iterator
属性,那么就可以认为是“可遍历的”。Symbol.iterator
属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。属性名Symbol.iterator
是一个表达式,返回Symbol
对象的iterator
属性。这是一个预定义好的、类型为Symbol的特殊值,所以要放在方括号内。
1 | const obj = { |
上面obj
对象是可遍历的,因为具有Symbol.iterator
属性。执行这个属性,会返回一个遍历器对象:
1 | const o = obj[Symbol.iterator](); |
每次调用next
方法,都会返回一个代表当前成员的信息对象:
1 | o.next(); // {value: 1, done: true} |
ES6的有些数据结构原生具备Iterator接口,即不用任何处理,就可以被for...of
循环遍历。原因在于这些数据结构原生部署了Symbol.iterator
属性。原生具备Iterator
接口的数据结构如下:
- Array
- Map
- Set
- String
- TypedArray (描述一个底层的二进制数据缓存区的内存区域)
- 函数的
arguments
对象 - NodeList对象(节点的集合,是由
Node.childNodes
和document.querySelectorAll
返回的
我们可以手动调用数组的Symbol.iterator
属性:
1 | const arr = ['a', 'b', 'c']; |
变量arr
是一个数组,原生具有迭代器接口,所以直接调用Symbol.iterator
这个属性,就可以得到遍历器对象。
对于原生部署Iterator接口的数据结构,不需要自己写遍历器函数,for...of
循环会自动遍历他们。其他数据结构的Iterator,都需要自己在Symbol.iterator
属性上面部署,这样才能被for...of
循环遍历。
使用迭代器接口实现一个范围函数:
1 | class RangeIterator { |
了解了迭代器接口(Iterator)以及为何可以使用for...of
来遍历数组。