您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

ES6+ Generator 基础

前面我们花了三节深入地学习了 ES6 的异步案 Promise,本节学习的器也是为了异步而生的,但是它的出发思路和 Promise 截然不同。@H__10@

上节我们学习了 ES6 中 的相关,并实现了迭代器。我们知道实现迭代器,我们需要手动对象的 Symbol.iterator ,并需要实现 next 。那么有没有什么可以帮助我们实现迭代器呢?ES6 给出了器的来满足我们的需求。我们不需要在对象上 Symbol.iterator ,使用器就可以实现迭代器的。本节我们将学习器的相关概念和基础。@H__10@

器是灵活的结构,能使得块内部暂停和恢复执行的能力。在实际应用中,使用器可以迭代器和协程。@H__10@

有些概念是我们必须要理解的,前面在学习迭代器的时候,我们学习了迭代协议和迭代器协议,实现迭代器需要满足这两个协议才算是真正的迭代器。而本节的器和器也是如此,我们也需要知道器对象和器概念和它们直接的关系。@H__10@

Generator 就是我们说的器,它包含两个概念 器对象和器。首先,要理解的是器对象和迭代器的关系,器对象是遵守迭代协议和迭代器协议实现的 Iterable 接口,可以理解器对象其实也是迭代器;然后,我们需要理解什么是器,器是由 function * 来定义的,并且返回结果是 Generator 对象。@H__10@

器是特殊的,在后会返回器对象,这个器对象是遵守可迭代协议和迭代器协议实现的 Iterable 接口。器可以使用 yield 关键字来暂停执行的器:@H__10@

function* generator() {
  yield 'a';
  yield 'b';
}

var gen = generator();	// Object [Generator] {}

器的 next () 和迭代器返回的结果是一样的,返回了包含 donevalue 的对象,该也可以通过接受参数用以向器传值。@H__10@

使用 yield 返回的值会被迭代器的 next () 捕获:@H__10@

var gen = generator();

gen.next()	// {value: 'a', done: false}
gen.next()	// {value: 'b', done: false}
gen.next()	// {value: undefined, done: true}

从上面的执行结果可以看出,器在执行后会返回器对象,这个器对象满足迭代协议和迭代器协议,所以我们可以去手动它的 next () 去每一步的返回值。从这里可以看出,器其实就是迭代器的应用,并且这个应用会在异步中大放异彩。@H__10@

return() 返回给定的值并结束器。@H__10@

var gen = generator();

gen.next();        // { value: 'a', done: false }
gen.return("imooc"); // { value: "imooc", done: true }
gen.next();        // { value: undefined, done: true }

另外,如果对已经完成状态的器 return(value) 则器会一直保持在完成状态,如果出入参数,value 会设置成传入的参数,done 的值不变:@H__10@

var gen = generator();

gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: undefined, done: true }
gen.return(); // { value: undefined, done: true }
gen.return(); // { value: 1, done: true }

throw() 用来向器抛出异常,并恢复器的执行,返回带有 donevalue 两个的对象。@H__10@

function* generator() {
  while(true) {
    try {
       yield 'imooc'
    } catch(e) {
      console.log("Error caught!");
    }
  }
}
var gen = generator();
gen.next(); // { value: "imooc", done: false }
gen.throw(new @H_934_@Error("error")); // "Error caught!"

将类数组转化为真正的数组方式有很多,ES6 提供了 Array.from() 可以将类数组转化为数组 。另外在一些中可以使用 [...argument] 的方式转化类数组。@H__10@

function fn() {
  const arg = [...arguments];
  console.log(arg);
}
fn(, , );	// [1, 2, 3]

当然我们知道类数组的定义,所以我们自己定义类数组,看能不能使用展开运算符将类数组转化为数组:@H__10@

const likeArr = {
  : ,
  : ,
  length: ,
}
console.log([...likeArr]);	// Uncaught TypeError: likeArr is not iterable

上面中我们定义了类数组,但是使用展开运算符报错了,我们 likeArr 不是迭代器。因为在中类数组是内部帮我们实现了迭代器的,而我们自己定义的类数组是不具有迭代器的,那我们来自己实现:@H__10@

likeArr[Symbol.iterator] = function() {
  let index = ;
  return {
    next: () => {
      return { value: this[index], done: index++ === this.length}
    }
  }
}
console.log([...likeArr]);	// [1, 2]

上面的我们在 likeArr 对象上定义了 Symbol.iterator 它具有迭代。上面中我们需要手动地去实现 next () ,这比较麻烦,那能不能简化一下呢?我们的器就出场了:@H__10@

likeArr[Symbol.iterator] = function* () {
  let index = ;
  while (index !== this.length) {
    yield this[index++];
  }
}
console.log([...likeArr]);	// [1, 2]

上面的使用了器,并且没有去手动实现 next () ,从这里我们也能很清楚地知道迭代器和器的关系。而且使用器更加简单方便。@H__10@

还有案例是面试中经常会考到的:@H__10@

题目:实现,每次下质数,要求不使用,且本身不接受任何参数@H__10@

从题目的要求可以知道,这个每次都会返回质数,也就是说每次后都会返回。@H__10@

首先我们定义判断数是否为质数的:@H__10@

function isPrime(num) {
  for (let i = ; i <= Math.sqrt(num); i++) {
    if (num % i === ) {
      return false
    }
  }
  return true
}

传统的方式是使用闭包来:@H__10@

function primeHandler() {
  let prime = 
  return () => {
    while (true) {
      prime++
      if (isPrime(prime)) {
        return prime
      }
    }
  }
}

const getPrime = primeHandler()
console.log(getPrime());	// 2
console.log(getPrime());	// 3
console.log(getPrime());	// 5

既然是单步执行的,那么我们就可以使用迭代器方式实现:@H__10@

var prime = {}
prime[Symbol.iterator] = function() {
  let prime = ;
  return {
    next() {
      while(true) {
        prime++
        if (isPrime(prime)) {
          return prime;
        }
      }
    }
  }
}
var getPrime = prime[Symbol.iterator]().next;
console.log(getPrime());	// 2
console.log(getPrime());	// 3

上实例我们知道实现迭代器的方式是很麻烦的,可以使用器去替代迭代器的,所以上面的可以使用器改造如下:@H__10@

function* primeGenerator () {
  let prime = 
  while (true) {
    prime++
    if (isPrime(prime)) {
      yield prime
    }
  }
}

var getPrime = primeGenerator().next().value
console.log(getPrime());	// 2
console.log(getPrime());	// 3

本节我们主要学习了器的概念和,需要器对象是由器返回的结果,器对象是遵守迭代协议和迭代器协议实现的 Iterable 接口。器其实就是对迭代器的应用。另外,通过两个案例更加深刻地理解了器的应用场景,对比了器和迭代器的不同。@H__10@


联系我
置顶