作者:阮一峰
本文仅用于学习记录,不存在任何商业用途,如侵删
JavaScript 原有的表示“集合”的数据结构,主要是数组(Array
)和对象(Object
),ES6 又添加了Map
和Set
。
这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map
,Map
的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
遍历器(Iterator)就是这样一种机制。
它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:
for...of
循环,Iterator 接口主要供for...of
消费。Iterator 的遍历过程是这样的:
next
方法,可以将指针指向数据结构的第一个成员。next
方法,指针就指向数据结构的第二个成员。next
方法,直到它指向数据结构的结束位置。每一次调用next
方法,都会返回数据结构的当前成员的信息。
具体来说,就是返回一个包含value
和done
两个属性的对象。其中,value
属性是当前成员的值,done
属性是一个布尔值,表示遍历是否结束。
下面是一个模拟next
方法返回值的例子:
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}
上面代码定义了一个makeIterator
函数,它是一个遍历器生成函数,作用就是返回一个遍历器对象。对数组['a', 'b']
执行这个函数,就会返回该数组的遍历器对象(即指针对象)it
。
指针对象的next
方法,用来移动指针。
开始时,指针指向数组的开始位置。然后,每次调用next
方法,指针就会指向数组的下一个成员。第一次调用,指向a
;第二次调用,指向b
。
next
方法返回一个对象,表示当前数据成员的信息。
这个对象具有value
和done
两个属性,value
属性返回当前位置的成员,done
属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用next
方法。
总之,调用指针对象的next
方法,就可以遍历事先给定的数据结构。
对于遍历器对象来说,done: false
和value: undefined
属性都是可以省略的,因此上面的makeIterator
函数可以简写成下面的形式。
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++]} :
{done: true};
}
};
}
由于 Iterator 只是把接口规格加到数据结构之上,所以,遍历器与它所遍历的那个数据结构,实际上是分开的,完全可以写出没有对应数据结构的遍历器对象,或者说用遍历器对象模拟出数据结构。
下面是一个无限运行的遍历器对象的例子。
var it = idMaker();
it.next().value // 0
it.next().value // 1
it.next().value // 2
// ...
function idMaker() {
var index = 0;
return {
next: function() {
return {value: index++, done: false};
}
};
}
上面的例子中,遍历器生成函数idMaker
,返回一个遍历器对象(即指针对象)。
但是并没有对应的数据结构,或者说,遍历器对象自己描述了一个数据结构出来。
如果使用 TypeScript 的写法,遍历器接口(Iterable)、指针对象(Iterator)和next
方法返回值的规格可以描述如下。
interface Iterable {
[Symbol.iterator]() : Iterator,
}
interface Iterator {
next(value?: any) : IterationResult,
}
interface IterationResult {
value: any,
done: boolean,
}