强大而难懂的迭代器

迭代器

什么是迭代器

js迭代器是一个对象,具有next方法,可以返回序列中的带有done和value两个属性的下一项。

1
2
3
4
5
6
7
8
9
10
11
function makeIterator(array){
var nextIndex = 0;4
return {
next: function(){
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{done: true};
}
};
}
迭代器接口

可迭代对象如Map,Set,Array,String,arguments,DOM elements的实例拥有一个名为Symbol.iterator的接口函数,调用该函数会返回迭代器

1
2
3
4
5
6
7
let a = [1,3,4]
var b = a[Symbol.iterator]()
console.log(b + '') //[object Array Iterator]
console.log(b.next()) //{ value: 1, done: false }
console.log(b.next()){ value: 3, done: false }
console.log(b.next()){ value: 4, done: false }
console.log(b.next()){ value: undefined, done: true }

拥有该接口函数的,可以使用for of循环,优点是可以使用break,只会遍历可迭代对象。

1
2
3
for (let i of a) {
console.log(i)
}

输出1,3,4

还可以使用展开运算符,yield*以及结构赋值

生成器

function*

function* 可以生成generator生成器对象。函数在调用generator的next方法时才会运行,运行到yield处会暂停,注意,会把yield处的语句执行。等到下一个next方法运行时才继续执行,
调用 next()方法时,如果传入了参数,那么这个参数会作为上一条执行的 yield 语句的返回值,例如:

1
2
3
4
5
6
7
8
9
10
11
function *gen(){
yield 10;
y=yield 'foo';
yield y;
}
var gen_obj=gen();
console.log(gen_obj.next());// 执行 yield 10,返回 10
console.log(gen_obj.next());// 执行 yield 'foo',返回 'foo'
console.log(gen_obj.next(10));// 将 10 赋给上一条 yield 'foo' 的左值,即执行 y=10,返回 10
console.log(gen_obj.next());// 执行完毕,value 为 undefined,done 为 true
generator生成器对象

生成器对象即是可迭代对象又是迭代器,由generator function返回,具有next方法和return方法。
next方法可以获得下一项

1
2
3
4
5
6
7
8
9
10
11
function* gen() {
yield 1;
yield 2;
}
let g = gen()
g.next()
console.log(g.next()) // {value:1,done: false}
console.log(g.next()) // {value:2,done: false}
console.log(g.next()) // {value:undefined,done: true}

return结束迭代。

1
console.log(g.return()) // {value:undefined,done: true}

实战

斐波那契数列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function* fibo() {
let a = 0
let b = 1
yield a
yield b
while (true) {
let next = a + b
a = b
b = next
yield next
}
}
let generator = fibo()
for (var i = 0; i < 10; i++)
console.log(generator.next().value) //=> 0 1 1 2 3 5 8 13 21 34 55
异步

一个每次调用加10的函数。可以看出,gen函数中的写法是同步的风格,然而实际上t函数中用到了异步,这里用到了yield的暂停和重新启动功能,十分强大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const gen = function*(num) {
console.log(num)
var a = yield t(num)
console.log(a)
var b = yield t(a)
console.log(b)
return b
};
var genResult = gen(10);
genResult.next()
function t (num) {
setTimeout(() => {
genResult.next(num + 10)
}, 100)
}

输出10,20,30

跟promise结合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function t (num) {
return new Promise(res => res(num + 10))
}
function runGenerator(g, ...res) {
var it = g(...res), ret;
(function iterate(val){
ret = it.next( val );
if (!ret.done) {
if ("then" in ret.value) {
ret.value.then( iterate );
}
else {
setTimeout( function(){
iterate( ret.value );
}, 0 );
}
}
})();
}
runGenerator(gen, 30)

引用

通过ES6 Generator函数实现异步操作

生成器

最后

本文章为前端进阶系列的一部分,欢迎关注和star本博客或是关注我的github

支持作者

如果我的文章对你有帮助,欢迎 关注和 star 本博客 或是关注我的 github,获取更新通知。欢迎发送邮件到hpoenixf@foxmail.com与作者交流