7 迭代器与生成器

65 阅读5分钟

迭代

迭代是指按顺序反复执行一段代码(循环),但是如果直接使用for对某个结构进行迭代,那必须要知道结构的大小以及获取元素的方式,所以希望找出通用的方式,来进行循环,从而衍生出迭代器。

迭代器协议

实现了next(),返回的是{done:_,value:_}

迭代器Iterator

  • 定义:实现了可迭代协议的对象
  • 迭代器并不是可迭代对象的快照,而是通过游标来记录遍历可迭代对象的历程,如果可迭代对象在迭代过程被修改,那迭代器也会反应相应的变化

可迭代协议

存在键Symbol.iterator,对应值指向的是返回迭代器的工厂函数 ,可继承

可迭代对象

定义

实现了可迭代协议的对象,即调用对象[Symbol.iterator]()获得迭代器

特性
  • 实际代码中并不会显式生成迭代器,实现可迭代协议的所有类型都会自动兼容接收可迭代对象的任何语言特性,包括:

    • for-of
    • 数组结构
    • 扩展操作符...
    • Array.from()、创建集合、映射
    • Promise.all()
    • Promise.race()
    • yield*
    let arr = ["foo",'bar','baz'];     //可迭代对象
    //接收可迭代对象的原生语言特征
    //for-of
    for(const item of arr) 
      console.log(item);                      //foo bar baz
    //数组解构
    const [a,b] = arr;
    console.log(a,b);                         //foo bar
    //拓展操作符
    console.log([...arr]);                    //[ 'foo', 'bar', 'baz' ]
    //创建Array,Set,Map
    console.log(Array.from(arr))              //[ 'foo', 'bar', 'baz' ]
    console.log(new Set(arr))                 //Set(3) { 'foo', 'bar', 'baz' }
    let pairs = arr.map((item,i)=>[item,i]);
    console.log(new Map(pairs))               //Map(3) { 'foo' => 0, 'bar' => 1, 'baz' => 2 }
    
    
  • 提前“关闭”

关闭并不是指真的销毁迭代器,而是指提前退出迭代,有以下情况会提前“关闭”

  1. break、continue、return和throw
  2. 解构操作没有消费所有值
    当出现上述情况时,迭代器如果实现了return(),就会调用return(),从而真正的销毁迭代器。但是如果没有实现return(),则只是提前退出,并没有关闭迭代,下次迭代时依然是使用该迭代器。
    所以测试一个迭代器是否真的关闭,只需要检查迭代器的属性return是否是一个函数对象,是则说明该迭代器会关闭,但是如果手动加上return(),则不会关闭,但是在提前推出的时候会调用
let arr = [1,2,3,4,5];
let iter = arr[Symbol.iterator]();
iter.return = function(){
  console.log("提前结束");
  return {done:true};
}
for(const item of iter){
  console.log(item); 
  if(item>2) break;
}//1 2 3 提前结束
for(const item of iter){
  console.log(item); 
}//4 5
创建可迭代对象
class ObjIter{//可迭代对象:实现了可迭代协议
    constructor(limit){
      this.limit = limit;
    }
    [Symbol.iterator](){//可迭代协议:Symbol.iterator值指向了返回迭代器的工厂函数
        let count = 1,
            limit = this.limit;
        return{    //迭代器:实现了迭代器协议的对象
            next(){//迭代器协议:next(),返回iteratorResult对象
                if(count<limit){
                  return{done:false,value:count++};//iteratorResult对象
                }
                return {done:true};
            },
            return() {
              console.log("提前退出");
              return{done:true};
            }
        }
    }
}
const objIter1 = new ObjIter(5);
for(const item of objIter1){
  console.log(item);              //1 2 3 4
}
let [a,b,c] = objIter1;           //提前退出
console.log(a,b,c);               //1 2 3

生成器

区分生成器和生成器对象,调用生成器会产生一个生成器对象,该对象

  1. 迭代器特例,内部实现了next()方法;
  2. 同时也是可迭代对象,实现了Symbol.iterator返回自身

形式

函数,只是函数名前加一个星号(*),星号不受两侧空格的影响

function *name1{};                 // 函数声明
let generatorFn = function *(){};  // 函数表达式,箭头函数不行
let foo ={
    *generatorFn(){}               // 对象字面量
}
class Foo ={
    *generatorFn(){}
}

生成器对象

生成器对象创建后处于暂停执行的状态。

  1. 调用next()后会执行到yeild后暂停,同时将yield返回的值填充到{done:,value:}的属性value中将其返回;
  2. 下次调用next(参数)时,将参数传入yeild,然后重复上面步骤。
  • 作为可迭代对象,进行相应操作
  • yield传参:yield可以接受来自next()调用时(除第一次)传入的参数
function *generator(initial){//生成器
  console.log(initial);
  console.log(yield);
  console.log(yield '返回值');
}
const generator1 = generator("foo");//生成器对象,创建后处于暂停状态
for(const item of generator1){}//foo undefined undefined
const generator2 = generator("foo");
generator2.next('first');//foo,并不会接受first
const result = generator2.next('second');//second,同时获得iteratorResult对象
console.log(result) // { value: '返回值', done: false }
generator2.next('third');//third
  • yield* 可迭代对象:“权力下放”,执行的是可迭代对象生成的迭代器的next(),迭代器返回 {done:true}时才会“权力收回”,执行内部代码到yield或代码结尾
function *generator(initial){
  yield *[1,2,3]
  yield initial+"返回"
}
const generator1 = generator("初始值");
for(const item of generator1){
  console.log(item); //1 2 3 初始值返回
}
  • 方法return(参数):会将参数写入iteratorResult对象的value值中并返回,然后将生成器对象设置为关闭状态,后续调用next只会返回 {done:true}
  • 方法throw(参数):会向yield传入错误,如果内部生成器对象内部没有处理,则会进入关闭状态

生成器作为默认迭代器

默认迭代器指的是\[Symbol.iterator](),该函数是返回生成器的工厂函数,而生成器调用后就返回生成器对象(也是迭代器),所以可以将生成器作为默认迭代器

class ObjIterable{
  constructor(value){
    this.value = value;
  }
  *[Symbol.iterator](){//生成器作为默认迭代器
    yield* this.value
  }
}
const obj = new ObjIterable([1,2,3,4]);
for(const item of obj){
  console.log(item);
} //1 2 3 4

接收可迭代对象的原生语言特征执行过程

  1. 首先调用默认迭代器获得迭代器
  2. 对迭代器调用next(),获得返回的iteratorResult对象
    2.1. iteratorResult.done为真,则退出
    2.2. iteratorResult.done为假,则将iteratorResult.value取出进行后续操作
  3. 以for-of为例,将取出的value赋值给of之前的变量