ES6 中的迭代器

迭代器

E6引入了迭代器的一些标准接口。迭代器是组织有序的代码的一种方式,可以理解成一次做一件事。

接口

接口中必须要有:

1
2
Iterator [required]
next() {method}: retrieves next IteratorResult

另外,有两个可选的成员:

1
2
3
Iterator [optional]
return() {method}: stops iterator and returns IteratorResult
throw() {method}: signals error and returns IteratorResult

还有迭代的结果:

1
2
3
4
IteratorResult
value {property}: current iteration value or final return value
(optional if `undefined`)
done {property}: boolean, indicates completion status

在当前的JS中并不支持接口的语法,所以你需要在你自己的代码中维护这些接口。当JS希望得到一个迭代器的时候(例如 for..of.. 循环),你必须实现了这些接口,否则代码会运行失败。

还有一个 Iterable 接口, 表示对象必须能够返回一个迭代器:

1
2
Iterable
@@iterator() {method}: produces an Iterator

@@iterator 是一个内建的 symbol, 代表方法必须能够生产一个迭代器。

IteratorResult

迭代器返回的结果 IteratorResult 是对象的形式:

1
{ value: .. , done: true / false }

内置的迭代器返回值都是这个形式,当然,对象中也可以添加别的属性。

next() 迭代

一个可迭代的数组,使用迭代器能够遍历整个数组:

1
2
3
4
5
6
7
8
9
var arr = [1,2,3];

var it = arr[Symbol.iterator]();

it.next(); // { value: 1, done: false }
it.next(); // { value: 2, done: false }
it.next(); // { value: 3, done: false }

it.next(); // { value: undefined, done: true }

arrSystem.iterator 方法会返回一个迭代器,大多数的数据结构都是这样的。

从上面这段代码可以看到 it 迭代器直到你接收到 3 之后才把 done 设成 true , 这个时候已经遍历完整个数组了。

字符串字面量默认也是可迭代的:

1
2
3
4
5
6
var greeting = "hello world";

var it = greeting[Symbol.iterator]();

it.next(); // { value: "h", done: false }
it.next(); // { value: "e", done: false }

ES6 引入了一些新的数据结构–集合。这些数据结构不仅可迭代,而且提供返回迭代器的API:

1
2
3
4
5
6
7
8
9
var m = new Map();
m.set( "foo", 42 );
m.set( { cool: true }, "hello world" );

var it1 = m[Symbol.iterator]();
var it2 = m.entries();

it1.next(); // { value: [ "foo", 42 ], done: false }
it2.next(); // { value: [ "foo", 42 ], done: false }

Optional: return(..) and throw(..)

return(..) 会发送一个信号给迭代器,告诉迭代器不要再继续了,可以停止工作了,然后开始一些清理工作,例如(释放网络连接等等)。

如果一个迭代器有 return 方法,那么在不正常或者提前终止迭代的时候,迭代器会自动调用这个方法。你也可以手动调用这个方法。

当异常或者错误发生的时候,迭代器会调用 throw()

下面看一个自定义的迭代器的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var Fib = {
[Symbol.iterator]() {
var n1 = 1, n2 = 1;

return {
// make the iterator an iterable
[Symbol.iterator]() { return this; },

next() {
var current = n2;
n2 = n1;
n1 = n1 + current;
return { value: current, done: false };
},

return(v) {
console.log(
"Fibonacci sequence abandoned."
);
return { value: v, done: true };
}
};
}
};

for (var v of Fib) {
console.log( v );

if (v > 50) break;
}
// 1 1 2 3 5 8 13 21 34 55
// Fibonacci sequence abandoned.