Iterators & Generators.md

Iterators & Generators

Iterators

一个迭代器对象 ,知道如何每次访问集合中的一项, 并跟踪该序列中的当前位置。在 JavaScript 中 迭代器是一个对象,它提供了一个next() 方法,用来返回序列中的下一项。这个方法返回包含两个属性:done和 value。
迭代器对象一旦被创建,就可以反复调用next()

来看个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function makeIterator(array){
var nextIndex = 0;
return {
next: function(){
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{done: true};
}
};
}
// 初始化后, next方法可以用来依次访问对象中的key
var it = makeIterator(['yo', 'ya']);
console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done); // true

来看Kyle的例子

1
2
3
4
5
6
7
8
var str = 'Hello';
var world = ['W', 'o', 'r', 'l', 'd'];

var it1 = str[Symbol.iterator]();
var it2 = world[Symbol.iterator]();

it1.next(); // {value: 'H', done: false}, 到最后一个的时候 {value: undefined, done: true}
it2.next(); // {value: 'W', done: false}

Declarative Iterators

1
2
3
4
5
6
7
8
var str = 'Hello';

for(let it = str[Symbol.iterator](), v, result;
(result = it.next()) &&
!result.done &&
(v = result.value || true); ) {
console.log(v);
}

在ES6我们新增了for-of循环, 替换上面的结构:

1
2
3
4
5
6
7
8
9
10
var str = 'Hello';
var it = str[Symbol.iterator]();

for(let v of it) {
console.log(v);
}

for(let v of str) {
console.log(v)
}

这里说一个解构小知识, …运算符运用了iterator

1
2
3
4
var str = 'Hello';

var letters = [...str];
console.log(letters);

自定义迭代器让我们定义自己的迭代规则。这种结构特别适用于我们自己创建的数据结构。

Data Structure without lterators

不是所有的数据结构都有iterator, 比如对象

1
2
3
4
5
6
7
8
9
10
11
var obj = {
a: 1,
b: 2,
c: 3
}

for (let v of obj) {
console.log(v);
} // Type Error

[...obj] // Type Error

在这种情况下, 我们可以自定义自己的iterator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var obj = {
a: 1,
b: 2,
c: 3,
[Symbol.iterator]: function() {
var keys = Object.keys(this);
var index = 0;

return {
// 在这里使用箭头函数的原因是因为, 需要使用到this关键字取到对象本身
next: () => (index < keys.length) ?
{done: false, value: this[keys[index++]]}:
{done: true, value: undefined}
}
}
};

[...obj] // [1, 2, 3]

Generators

虽然自定义的迭代器是一个有用的工具,但由于需要显式地维护其内部状态,因此需要谨慎地创建。Generators提供了一个强大的选择:它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。
GeneratorFunction 是一个可以作为迭代器工厂的特殊函数。当它被执行时会返回一个新的Generator对象。 如果使用function*语法,则函数将变为GeneratorFunction。

来看个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function* idMaker(ids = []) {
let len = ids.length;
if(len) {
for(let i = 0; i < len; i++) {
yield i;
}
} else {
yield undefined;
}
}

let gen = idMaker([0, 1, 2]);

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
// ...

OK, 我们来看一下Kyle的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function *main () {
yield 1;
yield 2;
yield 3;
return 0;
}
// 明确一点, 当我们调用Generator的时候,其实就是实例化了一个iterators实例, 他的行为和我们上面说的iterator很像。只不过我们在iterator上再写一层语法糖, 就是generator
var it = main();
it.next(); // {value: 1, done: false}
it.next(); // {value: 2, done: false}
it.next(); // {value: 3, done: false}
it.next(); // {value: 0, done: true}

[...main()]

好的, 我们用Generator重写一次上面obj没有默认iterator的例子

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
a: 1,
b: 2,
c: 3,
*[Symbol.iterator]() {
for(let key of Object.keys(this)) {
yield this[key];
}
}
}

[...obj]; // [1, 2, 3]