强大而难懂的迭代器

2,254 阅读2分钟

迭代器

什么是迭代器

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

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的接口函数,调用该函数会返回迭代器

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,只会遍历可迭代对象。

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

输出1,3,4

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

生成器

function*

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

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方法可以获得下一项

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结束迭代。

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

实战

斐波那契数列
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的暂停和重新启动功能,十分强大。

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结合

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