迭代器和生成器

什么是可迭代对象?

数组或集合(set)这样的集合类型的对象,是可迭代对象,但是,可迭代对象不一定是集合对象。

有哪些实现iterator接口的数据类型?

ArrayStringMapSetargumentsnodeList等dom集合模型

Symbol.iterator是啥?

通过访问Symbol.iterator,可以知道内置的迭代

  • 实现iterator接口的数据类型,都有Symbol.iterator属性
  • Symbol.iterator属性引用的是迭代器的工厂函数
  • 调用这个工厂函数,返回一个新迭代器

看下面的例子:

  const arr = [1,2,3]

  const m = new Map().set(1,'a').set(2,'b')

  console.log(arr[Symbol.iterator])  // Symbol.iterator属性在数组中指代的是values属性

  console.log(m[Symbol.iterator]())   // Symbol.iterator属性引用的是迭代器的工厂函数

  console.log(arr[Symbol.iterator] === arr.values) 

  for(let i of m.entries()){

    console.log(i);

  }

对应的执行结果:

  • 可以用Symbol.iterator属性来检查某类型的变量是否实现了迭代器工厂函数

如果没有实现时,Symbol.iterator属性是undefined

  const flag = true

  const num = 1

  const str = new Set().add(2).add(3)

  console.log(flag[Symbol.iterator]);  // undefined

  console.log(num[Symbol.iterator]);  // undefined

  console.log(str[Symbol.iterator]);  // ƒ values() { [native code] }
  • 原型链的父类实现了iterator接口,子类也会实现
class childArray extends Array {}

  const inst = new childArray('a', 'b', 1, 5)

  for(let i of inst){

    console.log(i);  //依次输出:  'a'  'b'  1   5

  }

迭代器是什么?

迭代器是一次性使用的对象,用于迭代与它相关的可迭代对象(数组、set,map 等)

迭代器怎么使用?

  • 迭代器用next()方法,对可迭代对象进行遍历。
  • 调用next( )方法后,返回的是对象,格式类似这种: {done: false, value: 'hello'} 

迭代器有什么特性?

1,调用它的next()方法遍历它对应的可迭代对象时,一开始resultObj的done是false,迭代完毕后done是true

2,done为true后,继续调用next(),返回的resultObj不变

3,不同迭代器实例之间没有影响

4,某次迭代后,可迭代对象改了(例如插入了某个值),继续迭代后,迭代的是修改后的可迭代对象

5,迭代器工厂函数(iterableObj[Symbol.iterable]) 执行后,得到迭代器,迭代器也实现了iterator接口,且执行迭代器工厂函数后,返回的是相同的迭代器。看例子

const arr = [3,4,5,6]

const iter = arr[Symbol.iterator]()

console.log(iter === iter[Symbol.iterator]());

如何提前终止迭代器?

总的来说,就是调用迭代器的return方法。比如:
1,for...of 的breakcontinuereturn 或者 throw

2,解构操作,并未消费所有值  

(可参考书中的例子进行理解)

终止迭代器有哪些注意的点?

1,如果迭代器没关闭,会从上次离开的地方,继续迭代

2, 迭代器有return方法时,都会被调用

2,有些迭代器是不可关闭的,但是,给迭代器加了return函数之后,终止迭代器,return会被调用,但是不可关闭属性还是不变。看例子:

  const arr = [1,3,5,6]

  const iter = arr[Symbol.iterator]()

  console.log(iter.return) // undefined

  iter.return = function () {

    console.log("this is the end")

    return {done: true, value: "xx"} // return返回的数据,必须含done:true

  }

  for(let i of iter){

    console.log('i:',i);

    if(i > 4){

      break;

    }

  }

  for(let i of iter){

    console.log('i==',i); // 即便给return属性赋值,iter的不可关闭属性还是不变,此处还是继续上面的for...of迭代

  }

迭代器有什么作用?

1,迭代器可以遍历对应的迭代对象。在实际代码中,迭代器的作用是隐性的。比如下面语法会自动调用迭代器的工厂函数,创建迭代器 :

for...of

数组解构

扩展运算符

Array.from()

创建集合

创建映射

Promise.all()

Promise.race()

yield* 操作符

2,以上操作会对实现了iterator接口的数据类型,执行:  创建迭代器 -> 调用迭代器next()方法 -> 取resultObj中value属性的值。,

3,迭代器是某些操作的底层原理,比如 for..of 循环之所以能够遍历实现了iterator接口的数据,是因为 for...of 操作自动创建了迭代器

生成器是什么?

生成器是一种函数,具体来说就是function后面加了星号*的函数。

定义函数的地方都能定义生成器(函数),箭头函数除外

生成器怎么使用?

1,生成器(函数)中,function后的*,不受两侧空格影响,

2,调用生成器(函数),会返回一个生成器对象,调用生成器对象的next() 方法,生成器(函数)才会开始执行

3,生成器对象next() 方法的返回值和迭代器调用next( )方法返回值一样,resultObj含done和value属性

4,生成器对象也实现了iterator接口,默认迭代器是自引用

    const generatorFn = function * () {

      return "xixi"

    }

    const gen = generatorFn()

    console.log(gen.next())    // { value: 'xixi', done: true }

    console.log(gen === gen[Symbol.iterator]())   // true

生成器的yield关键字,有什么作用?

yield关键字可以让生成器停止和开始执行,yield只能在生成器函数内部使用。

调用生成器对象的next() 方法,生成器函数会执行到yield关键字这一行,然后暂停

    const generatorFn = function *() {

      yield "first"

      yield "two"

      return "three"

    }

    const gen = generatorFn() 

    console.log(gen.next()); // { value: 'first', done: false }

    console.log(gen.next()); // { value: 'two', done: false }

    console.log(gen.next()); // { value: 'three', done: true }

yield结合生成器函数有哪些使用场景?

1,由于 生成器对象也实现了iterator接口, 所有 可以用for...of, Array.from( ) 等,来遍历生成器对象

    const generatorFn = function *() {

      yield 'first'

      yield 'two'

      yield 'three3'

    }

    const gen = generatorFn()

    console.log(Array.from(gen));  // [ 'first', 'two', 'three3' ]

2,使用yield实现输入和输出

两种情况:

  • next传值,赋给yield 
  • yield后面的值,被抛出来
    const generatorFn = function *(init) {

      let a = yield 3

      console.log('a:', a)

      let c = yield 4

      console.log('c:', c)

      let b = yield  8 * init

      console.log(b + 1);

      return b

    }

    const gen = generatorFn(3)

    console.log(gen.next(1));   // { value: 3, done: false }

    console.log(gen.next(2)); // a: 2, { value: 4, done: false }

    console.log(gen.next('a==**')); // c: a==** , { value: 24, done: false }

    console.log(gen.next(69)); // 70 , { value: 69, done: true }

以上例子说明:

  • 调用next(),得到的resultObj中value值是yield后面的表达式
  • 第一次调用next的传参不生效,gen.next(1),传入的1没有生效
  • 从第二次起,当next有传参,传参会作为上一行yield等式左边的变量的值
  • 总结下:yield后面表达式的值,会作为resultObj的value抛出;next(param) 传入的param,会赋值给上一个yield表达式左侧的变量,然后参与generatorFn函数接下来的计算

yield * 生成可迭代对象的用法?

  • yield 后面 * 两侧的空格不受影响
  • yield * 是将可迭代对象,单独序列化为一连串可单独产出的值
    const generatorFn = function *() {

      yield * [3,2,5]

    }

    for(let i of generatorFn()) {

      console.log('value: ', i) // 3,2,5

    }

从上面例子看出,可把yield *[3,2,5] 理解成 yield 3,  yield 2,  yield 5, 也就是yield *   将[3, 2, 5] 做了自动展开

生成器有什么作用?

生成器作为默认迭代器。

结合yield * ,实现递归操作。

     function  * nTimes(n) {
      if(n >0) {
        yield * nTimes(n -1)
        yield n - 1
      }
    }
    for(const x of nTimes(4)) {
      console.log(x)
    }

如何提前终止生成器?

1,生成器对象有return方法,终止了之后,后面再调用next方法,将会失效。

    const generatorFn = function *() {
      for(const x of [1,3,5]){
        yield x
      }
    }
    const g = generatorFn()
    console.log(g.next()); // { value: 1, done: false }
    console.log(g.next()); // { value: 3, done: false }
    console.log(g.return()); // { value: undefined, done: true }
    console.log(g.next()); // { value: undefined, done: true }

2,生成器对象的throw方法。将提示的错误注入到生成器对象,如果错误未被处理,则生成器关闭

你可能感兴趣的:(迭代器和生成器)