找回密码
 立即注册
首页 业界区 业界 迭代、迭代器、生成器的前世今生

迭代、迭代器、生成器的前世今生

僻嘶 2025-6-3 00:17:45
什么是迭代

类似于遍历 遍历:有多个数据组成的集合数据结构(map、set、array等其他类数组),需要从该结构中依次取出数据进行某种处理。 迭代:按照某种逻辑,依次取出下一个数据进行处理。
什么是迭代器 iterator

JS语言规定,如果一个对象具有next方法,并且next方法满足一定的约束,则该对象是一个迭代器(iterator)。 next方法的约束:该方法必须返回一个对象,该对象至少具有两个属性:

  • value:any类型,下一个数据的值,如果done属性为true,通常,会将value设置为undefined
  • done:bool类型,是否已经迭代完成
  • 通过迭代器的next方法,可以依次取出数据,并可以根据返回的done属性,判定是否迭代结束。
案例如下:
  1. /**
  2.        * 迭代器
  3.        * 必须有个next函数
  4.        * 该函数必须返回一个对象 包括 value 属性 和 done属性
  5.        * value:本次迭代的返回值
  6.        * done:true 或者 false 本次迭代是否结束
  7.        * */
  8.       const iterator = {
  9.         total:3,
  10.         index:1,
  11.         next(){
  12.           const obj = {
  13.             value:this.index > this.total?undefined:Math.random(),
  14.             done:this.index > this.total
  15.           }
  16.           this.index++;
  17.           return obj;
  18.         }
  19.       }
  20.       //一个一个迭代直到不能迭代为止
  21.       let result = iterator.next();
  22.       while(!result.done){
  23.         console.log(result);
  24.         result = iterator.next();
  25.       }
  26.       //输出斐波拉数列的数据
  27.       //1 1 2 3 5 8 13 21
  28.       const sequenceItereator = {
  29.         a:1,
  30.         b:1,
  31.         curIndex:1, //当前从1开始算
  32.         next(){
  33.           if(this.curIndex == 1 || this.curIndex == 2){
  34.             this.curIndex++;
  35.             return {
  36.               value:1,
  37.               done:false
  38.             }
  39.           }
  40.           var c = this.a + this.b;
  41.           this.curIndex++;
  42.           this.a = this.b;
  43.           this.b = c;
  44.           return {
  45.             value:c,
  46.             done:false
  47.           }
  48.         }
  49.       }
  50.       for(var i = 0;i<50;i++){
  51.         console.log(sequenceItereator.next());
  52.       }
复制代码
 
什么是可迭代协议

ES6中出现了for-of循环,该循环就是用于迭代某个对象的,因此,for-of循环要求对象必须是可迭代的(对象必须满足可迭代协议) 可迭代协议是用于约束一个对象的,如果一个对象满足下面的规范,则该对象满足可迭代协议,也称之为该对象是可以被迭代的。 可迭代协议的约束如下:

  • 对象必须有一个知名符号属性(Symbol.iterator)
  • done:bool类型,是否已经迭代完成
  • 该属性必须是一个无参的迭代器创建函数
案例如下:
  1. //迭代器创建函数
  2.       function createIterator(arr){
  3.         var i = 0;
  4.         return {
  5.           next(){
  6.             var obj = {
  7.               value:arr[i],
  8.               done:i>arr.length - 1
  9.             }
  10.             i++;
  11.             return obj;
  12.           }
  13.         }
  14.       }
  15.       var iterator = createIterator([1,3,5,7,9]);
  16.       console.log(iterator);
复制代码
 
什么是生成器

生成器:由构造函数Generator创建的对象,该对象既是一个迭代器,同时,又是一个可迭代对象(满足可迭代协议的对象)
**注意:Generator构造函数,不提供给开发者使用,仅作为JS引擎内部使用**
生成器函数(生成器创建函数):该函数用于创建一个生成器。
ES6新增了一个特殊的函数,叫做生成器函数,只要在函数名与function关键字之间加上一个*号,则该函数会自动返回一个生成器
生成器函数的特点:

  • 调用生成器函数,会返回一个生成器,而不是执行函数体(因为,生成器函数的函数体执行,收到生成器控制)
  • 每当调用了生成器的next方法,生成器的函数体会从上一次yield的位置(或开始位置)运行到下一个yield

    • yield关键字只能在生成器内部使用,不可以在普通函数内部使用
    • 它表示暂停,并返回一个当前迭代的数据
    • 如果没有下一个yield,到了函数结束,则生成器的next方法得到的结果中的done为true

  • yield关键字后面的表达式返回的数据,会作为当前迭代的数据
  • 生成器函数的返回值会作最终的value的值 但是当在进行next时 value是undefined
  • 生成器在调用next的时候可以传递参数,该参数会作为上一次yield整个表达式的返回结果
案列如下:
  1. //可迭代协议
  2.       var robj = {
  3.         [Symbol.iterator]:function(){
  4.           var total = 3;
  5.           var i = 1;
  6.           return {
  7.             next(){
  8.               var oop = {
  9.                 value:i>total?undefined:Math.random(),
  10.                 done:i>total
  11.               }
  12.               i++;
  13.               return oop;
  14.             }
  15.           }
  16.         }
  17.       };
  18.       for (const element of robj) {
  19.         console.log(element);
  20.       }
复制代码
  1. //生成器函数  调用该函数返回一个生成器  该生成器即使是一个迭代器 又是一个可迭代对象(满足可迭代协议)
  2.       function* createGenerator(){
  3.         console.log('函数体执行 - 开始');
  4.         yield 1; //会作为本次迭代的value值 {value:1,done:false}
  5.         console.log("函数体执行 - 1")
  6.         yield 2;//会作为本次迭代的value值 {value:2,done:false}
  7.         console.log('函数体执行 - 2');
  8.         yield 3;//会作为本次迭代的value值 {value:3,done:false}
  9.         console.log("函数体执行 - 3");
  10.         return "结束"//会作为本次迭代的value值 {value:"结束",done:true}
  11.       }
  12.       //掉用只会返回一个生成器 不会执行函数体
  13.       var generator = createGenerator();
  14.       //当调用next的时候会从开始位置到第一个yield处执行  执行到yield位置就会卡住(不会继续执行), 等到下一次next的时候
  15.       //生成器的函数体会从上一次yield的位置(或开始位置)运行到下一个yield
  16.       console.log(generator.next);
  17.       const iterator = generator[Symbol.iterator]();
  18.       function* createArrayIterator(array){
  19.         for (let index = 0; index < array.length; index++) {
  20.           const item = array[index];
  21.           console.log(`第${index}次迭代`);
  22.           yield item;
  23.         }
  24.       }
  25.       const arrayIterator = createArrayIterator([1,2,3,4,5,6]);
复制代码
  1. //生成器函数  调用该函数返回一个生成器  该生成器即使是一个迭代器 又是一个可迭代对象(满足可迭代协议)
  2.       function* createGenerator(){
  3.         console.log('函数体执行 - 开始');
  4.         let result = yield 1; //会作为本次迭代的value值 {value:1,done:false}
  5.         console.log("函数体执行 - 1",result)
  6.         result = yield 2;//会作为本次迭代的value值 {value:2,done:false}
  7.         console.log('函数体执行 - 2',result);
  8.         result = yield 3;//会作为本次迭代的value值 {value:3,done:false}
  9.         console.log("函数体执行 - 3",result);
  10.         return "结束"//会作为本次迭代的value值 {value:"结束",done:true}
  11.       }
  12.       let itereator = createGenerator();
  13.       let res = itereator.next();
  14.       function run(){
  15.         if(!res.done){
  16.           //如果在调用next的时候 给 next传递参数  该参数会作为 yield 整个表达式的值返回
  17.           //执行步骤
  18.           //第一次调用就不care了  第一次调用碰到yield 1; 就会卡住不会往下执行  这个时候还不执行赋值操作
  19.           //第二次调用next 传递上一次迭代的值作为参数传递  这个时候 就会从上一次 yield的位置 运行到下一个yield (这个时候就会进行赋值操作)
  20.           //整个yield 表达式的返回值 就是给next函数传递的参数
  21.           //依次类推
  22.           console.log(res);
  23.           res = itereator.next("张三:"+Math.random());
  24.           run();
  25.         }
  26.       }
  27.       run();
复制代码
  1. var i = 0;
  2.       function asyncData(){
  3.         return new Promise((resolve,reject)=>{
  4.           setTimeout(() => {
  5.             i++;
  6.             //3秒后完成 完成的数据
  7.             resolve('完成'+i);
  8.           }, 10000);
  9.         })
  10.       }
  11.       //调用next()方法时传入的值会作为上一个yield表达式的返回值
  12.       //创建一个生成器函数 调用时返回一个生成器
  13.       function* task(){
  14.         console.log("开始获取数据");
  15.         let data = yield asyncData();
  16.         console.log('获取到的数据',data);
  17.         data = yield asyncData();
  18.         console.log("又获取到了数据",data);
  19.         data = yield 1;
  20.         console.log('又获取到了数据',data);
  21.         return '结束';
  22.       }
  23.       //没封装之前的写法
  24.       /*function run(createGenerator){
  25.         const generator = createGenerator();
  26.         let res = generator.next();
  27.         function next(){
  28.           if(!res.done){
  29.             console.log(res);
  30.             const value = res.value;
  31.             if(typeof value.then === 'function'){
  32.               value.then((data)=>{
  33.                 res = generator.next(data);
  34.                 next();
  35.               });
  36.             }else{
  37.               res = generator.next(value);
  38.               next();
  39.             }
  40.           }
  41.         }
  42.         next();
  43.       }*/
  44.       //封装后的写法
  45.       function run(createGenerator){
  46.         const generator = createGenerator();
  47.         console.log(generator);
  48.         next();
  49.         function next(nextValue){
  50.           const res = generator.next(nextValue);
  51.           if(res.done){
  52.             console.log('生成器迭代结束');
  53.             return;
  54.           }
  55.           const value = res.value;
  56.           if(typeof value.then === 'function'){
  57.             //如果返回的是promise  将promise完成时的数据作为参数
  58.             //作为上一次yield表达式的返回值
  59.             value.then((data)=>{
  60.               return next(data)
  61.             })
  62.           }else{
  63.             console.log('走这里了')
  64.             //将上一次迭代获取到的value的值作为参数 传递给上一次yield表达式的返回值
  65.             next(res.value);
  66.           }
  67.         }
  68.       }
  69.       run(task);
复制代码
1.png

 
 
总结:

生成器的核心价值在于其‌延迟执行与状态保持能力‌,适用于:

  • 需要按需生成数据的迭代场景(如分页、树遍历)
  • 资源敏感型任务(如大文件处理、流式传输)
  • 复杂流程控制(如多步骤交互、状态机)
  • 尽管 async/await 更常用于异步编程,但生成器在定制化迭代器协议、性能优化框架中仍不可替代

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册