Iterator和Genertator

news2024/9/29 18:07:18

一、Iterator迭代器和for of原理


* 遍历器(Iterator)是一种机制(接口):为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署Iterator接口,就可以完成遍历操作「for of循环」,依次处理该数据结构的所有成员

* + 拥有next方法用于依次遍历数据结构的成员

* + 每一次遍历返回的结果是一个对象 {done:false,value:xxx}

* + done:记录是否遍历完成

* + value:当前遍历的结果

* 拥有Symbol.iterator属性的数据结构(值),被称为可被遍历的,可以基于for of循环处理

* + 数组

* + 部分类数组:arguments/NodeList/HTMLCollection...

* + String

* + Set

* + Map

* + generator object

* + ...

* 对象默认不具备Symbol.iterator,属于不可被遍历的数据结构

自己创建一个Iterator类,来实现ES6中的迭代器规范

class Iterator {
    constructor(assemble) {
        let self = this;
        self.assemble = assemble;
        self.index = 0;
    }
    next() {
        let self = this,
            assemble = self.assemble;
        if (self.index > assemble.length - 1) {
            return {
                done: true,
                value: undefined
            };
        }
        return {
            done: false,
            value: assemble[self.index++]
        };
    }
}

创建一个实例对象,其应该具备迭代器规范的要求 
  itor.next() 具备next方法,执行这个方法可以依次获取到数据结构中的每一个成员值
  每一次获取的成员值是一个对象 
    + done:是否迭代完毕
    + value:当前获取的那个值
  符合以上两个特点的对象,我们称之为符合迭代器规范的对象!!

let itor = new Iterator([10, 20, 30, 40]);
console.log(itor.next()); //->{value:10,done:false}
console.log(itor.next()); //->{value:20,done:false}
console.log(itor.next()); //->{value:30,done:false}
console.log(itor.next()); //->{value:40,done:false}
console.log(itor.next()); //->{value:undefined,done:true}

/*

 在JS中,有很多数据结构,天生具备迭代器规范,例如:

   我们主要看数据结构(对象)是否具备 Symbol.iterator 这个属性;有这个属性就具备迭代器规范,没有就不具备;具备这个规范,就可以使用 for/of 循环来迭代数据中的每一项值了!!

   + 数组  Array.prototype[Symbol(Symbol.iterator)]=function...

   + 部分类数组

     + arguments[Symbol(Symbol.iterator)]

     + NodeList.prototype[Symbol(Symbol.iterator)]

     + HTMLCollection.prototype[Symbol(Symbol.iterator)]

     + ...

   + 字符串 String.prototype[Symbol(Symbol.iterator)]

   + Set/Map

   + ...

 但是对于纯粹对象「或者自己构建的类数组对象」等来讲,默认是不具备 Symbol.iterator 这个属性的,所以他们不具备迭代器规范!「不能直接使用 for/of 循环」

 */

数组迭代的方式:for、while、forEach/map、for/in、for/of...

let arr = [10, 20, 30, 40];

arr[Symbol.iterator] = function () {
    console.log('FOR/OF START');
    let self = this, //this->arr
        index = -1;
    // 返回具备迭代器规范的对象 -> itor
    return {
        next() {
            index += 2;
            if (index >= self.length) {
                return {
                    done: true,
                    value: undefined
                };
            }
            return {
                done: false,
                value: self[index]
            };
        }
    };
};

for (let val of arr) {
    console.log(val);
} 

for of 原理

// for/of循环主要用于获取数据结构中每一项的“值”
for (let val of arr) {
    console.log(val);
} 
原理:
  1.迭代执行,先执行数组的Symbol.iterator这个方法,获取一个具备迭代器规范的对象->itor
  2.开始迭代:每一次迭代都是把 itor.next 方法执行
    + 把获取对象中的value属性值,赋值给val这个变量
    + 再看对象中done这个属性的值,如果是false,则继续迭代;如果是true,则结束迭代!!
*/

//============================
// 迭代对象的方式:for/in;获取所有的keys,然后迭代keys;也可以使用for/of(但是需要自己为其设置Symbol.iterator)
/* let obj = {
    name: 'zhufeng',
    age: 13,
    0: 100,
    [Symbol('AA')]: 200
};

Object.prototype[Symbol.iterator] = function iterator() {
    let self = this, //迭代的对象
        index = -1,
        keys = Reflect.ownKeys(self);
    return {
        next() {
            index++;
            if (index >= keys.length) {
                // 这个必须要设置,否则就是死循环了
                return {
                    done: true,
                    value: undefined
                };
            }
            let key = keys[index];
            return {
                done: false,
                value: self[key]
            };
        }
    };
};

for (let val of obj) {
    console.log(val);
} 

for (let val of obj) {
    console.log(val); //Uncaught TypeError: obj is not iterable
} 

类数组

/* // 如果是类数组,是可以直接借用数组原型上的Symbol.iterator方法的!!
let obj = {
    0: 10,
    1: 20,
    2: 30,
    length: 3
};
obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
for (let val of obj) {
    console.log(val);
} */

/* 
if ( typeof Symbol === "function" ) {
    jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
}
*/

二、 Genertator基础和 await原理


生成器对象是由一个generator function返回的,并且它符合可迭代协议和迭代器协议
Generator - JavaScript | MDN

讲义示例:

/*
 * 普通函数 VS 生成器函数
 *    生成器函数 [[IsGenerator]]:true
 *    
 *   「把它当做一个实例 __proto__」
 *       普通函数是 Function 的实例,普通函数.__proto__===Function.prototype
 *       生成器函数是 GeneratorFunction 的实例
 *           生成器函数.__proto__===GeneratorFunction.prototype
 *           GeneratorFunction.prototype.__proto__===Function.prototype
 *      ({}).toString.call(生成器函数) => "[object GeneratorFunction]"
 *    
 *   「把它作为一个构造函数 prototype」
 *      生成器函数不能被new执行  Uncaught TypeError: func is not a constructor
 *      当做普通函数执行,返回的结果就是生成器函数的一个实例
 *      itor.__proto__ -> func.prototype「空对象,没有constructor」 -> Generator.prototype「constructor:GeneratorFunction」{next/return/throw/Symbol(Symbol.toStringTag): "Generator"} -> 一个具备迭代器规范的对象「Symbol(Symbol.iterator)」 -> Object.prototype
 */

// generator函数:function后面加一个*
function* fn() {
    console.log(this);
    return 10;
}
fn.prototype.query = function () {};
let gen = fn();
console.log(gen);
// gen.__proto__ -> fn.prototype「query」 -> GeneratorFunction.prototype「next/return/throw/Symbol.toStringTag」-> xxx.prototype「Symbol.iterator」 -> Object.prototype
console.log(typeof fn); //->"function"
console.log(fn instanceof Function); //->true 


function* generator() {
    console.log('A');
    yield 10;
    console.log('B');
    yield 20;
    console.log('C');
    yield 30;
    console.log('D');
    return 100;
}
let itor = generator();
console.log(itor.next()); //->{value:10,done:false}
console.log(itor.next()); //->{value:20,done:false}
console.log(itor.next()); //->{value:30,done:false}
console.log(itor.next()); //->{value:100,done:true}
console.log(itor.next()); //->{value:undefined,done:true} 


function* generator() {
    console.log('A');
    yield 10;
    console.log('B');
    yield 20;
    console.log('C');
    return 30;
}
let itor = generator();
console.log(itor.next()); //->{value:10,done:false}
console.log(itor.return('@return')); //->{value:"@return",done:true}
// console.log(itor.throw('@throw'));
console.log(itor.next()); //->{value:undefined,done:true}


// 每一次执行next的传递的值,是作为上一次yeild的返回值处理的
function* generator() {
    let x1 = yield 10;
    console.log(x1);
    let x2 = yield 20;
    console.log(x2);
    return 30;
}
let itor = generator();
itor.next('@1');
itor.next('@2');
itor.next('@3');


// yeild* 后面跟着一个新的itor,后期执行到这的时候,会进入到新的generator中执行
function* generator1() {
    yield 10;
    yield 20;
}

function* generator2() {
    yield 10;
    yield* generator1();
    yield 20;
}
let itor = generator2();
console.log(itor.next()); //value:10  done:false
console.log(itor.next()); //value:10 done:false
console.log(itor.next()); //value:20  done:false
console.log(itor.next()); //value:20 done:false
console.log(itor.next()); //value:undefined done:true 

如何创建一个Generator生成器函数?

   + 把创建函数的“function”后面加一个 “*” 即可

   + 箭头函数是无法变为生成器函数的

 每一个生成器函数,都是GeneratorFunction这个类的实例

   fn.__proto__ -> GeneratorFunction.prototype -> Function.prototype

   多了这样的一个私有属性 [[IsGenerator]]:true

 普通函数的原型链

   fn.__proto__ -> Function.prototype

 

 当生成器函数执行:

   + 首先并不会立即把函数体中的代码执行

   + 而是返回一个具备迭代器规范的对象「itor」

     itor.__proto__

     + next

     + throw

     + return

     + Symbol(Symbol.iterator) : function...

     + ...

   + 当进行itor.next()执行的时候

     + 把函数体中的代码开始执行

     + 返回一个对象

       + done:记录代码是否执行完毕

       + value:记录本次处理的结果

const fn = function* fn() {
    console.log("代码运行中:", 10);
    return 100;
};
let itor = fn();
console.log(itor.next()); //-> {done:true,value:100}
console.log(itor.next()); //-> {done:true,value:undefined}

对于ES6快捷赋值的语法,我们在方法名前面设置*,就可以创建生成器函数了

let obj = {

    // sum:function(){}

    *sum() {

    }

};

console.log(obj.sum());

 Generator生成器函数的作用:

   可以基于返回的itor(迭代器对象),基于其next方法,控制函数体中的代码,一步步的去执行!!

   + 每一次执行next,控制函数体中的代码开始执行「或者从上一次暂停的位置继续执行」,遇到yield则暂停!!

     done:false

     value:yield后面的值

   + 当遇到函数体中的return,或者已经执行到函数最末尾的位置了

     done:true

     value:函数的返回值或者undefined

const fn = function* fn() {
    console.log('A');
    yield 100;
    console.log('B');
    yield 200;
    console.log('C');
    yield 300;
    console.log('D');
    return 400;
};
let itor = fn();
console.log(itor.next()); //->{done:false,value:100}
console.log(itor.next()); //->{done:false,value:200}
console.log(itor.next()); //->{done:false,value:300}
console.log(itor.next()); //->{done:true,value:400}

--------------------------------------------------------

const fn = function* fn() {
    console.log('A');
    yield 100;
    console.log('B');
    yield 200;
    console.log('C');
    yield 300;
    console.log('D');
    return 400;
};
let itor = fn();
console.log(itor.next());  // -> {done:false, value:100}
console.log(itor.return('哈哈哈')); // -> {value:'哈哈哈', done:true} 相当于在函数体中执行遇到了return,结束整个函数的继续执行「done:true」,传递的值相当于return的值!!
console.log(itor.next());  // -> {value:undefined, done:true}


console.log(itor.throw('哈哈哈')); // 手动抛出异常「控制台报红」;生成器函数中的代码,都不会再执行了!!
console.log(itor.next()); // 抛出异常后,它下面的代码也不会执行了
console.log('我是的外边的');

 params:生成器函数接收的实参值,它是生成器函数执行时传递的值

   fn(10,20,30)

   params:[10,20,30]

 itor.next(N):每一次执行next方法,传递的值会作为上一个yield的返回值「所以第一次执行next方法,传递的值是没有用的,因为在它之前没有yield」

const fn = function* fn(...params) {
    let x = yield 100;
    console.log(x); //'second:222'
    let y = yield 200;
    console.log(y); //'three:333'
};
let itor = fn(10, 20, 30);
console.log(itor.next('first:111')); //-> {value: 100, done: false}
console.log(itor.next('second:222')); //-> {value: 200, done: false}
console.log(itor.next('three:333')); //-> {value: undefined, done: true} 

 生成器嵌套问题  yield*

const sum = function* sum() {
    yield 300;
    yield 400;
};
const fn = function* fn() {
    yield 100;
    yield* sum(); // yield*:支持让我们进入另外一个生成器函数中去一步步的执行 
    yield 200;
};
let itor = fn();
console.log(itor.next()); //-> {value: 100, done: false}
console.log(itor.next()); //-> {value: 300, done: false}
console.log(itor.next()); //-> {value: 400, done: false}
console.log(itor.next()); //-> {value: 200, done: false}
console.log(itor.next()); //-> {value: undefined, done: true}

// -------------------------

异步

const delay = (interval = 1000) => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(`@@${interval}`);
        }, interval);
    });
};

需求:串行请求,有三个请求「请求需要的时间分别是 1000/2000/3000」?

delay(1000)
    .then(value => {
        console.log('第一个请求成功:', value);
        return delay(2000);
    })
    .then(value => {
        console.log('第二个请求成功:', value);
        return delay(3000);
    })
    .then(value => {
        console.log('第三个请求成功:', value);
    })
    .catch(reason => {
        console.log('任何请求失败,都执行这里:', reason);
    });
(async () => {
    try {
        let value = await delay(1000);
        console.log('第一个请求成功:', value);

        value = await delay(2000);
        console.log('第二个请求成功:', value);

        value = await delay(3000);
        console.log('第三个请求成功:', value);
    } catch (reason) {
        console.log('任何请求失败,都执行这里:', reason);
    }
})();

基于Generator函数,模拟Await的语法,实现请求的串行

const handle = function* handle() {
    let value = yield delay(1000);
    console.log('第一个请求成功:', value);

    value = yield delay(2000);
    console.log('第二个请求成功:', value);

    value = yield delay(3000);
    console.log('第三个请求成功:', value);
};

编写通知Generator中代码逐一执行的方法

ES8(ECMAScript2017)中,提供了async/await语法:用来简化promise的操作

   它是 Promise + Generator 的语法糖

   我们自己上边实现的AsyncFunction和Generator函数就是async/await的原理!!

const AsyncFunction = function AsyncFunction(generator, ...params) {
    let itor = generator(...params);
    // 基于递归的方法,通知Generator函数中的代码逐一执行
    const next = x => {
        let { done, value } = itor.next(x);
        if (done) return;
        if (!(value instanceof Promise)) value = Promise.resolve(value);
        value.then(next);
    };
    next();
};

// AsyncFunction(handle);
AsyncFunction(function* (x, y) {
    let total = x + y;
    let value = yield total;
    console.log('@1-->', value);

    yield delay(2000);
    console.log('@2-->', '哈哈哈');
}, 10, 20);

/* (async (x, y) => {
    let total = x + y;
    let value = await total;
    console.log('@1-->', value);

    await delay(2000);
    console.log('@2-->', '哈哈哈');
})(10, 20); */

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/374994.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++入门(下篇)】C++引用,内联函数,auto关键字的学习

前言: 在上一期我们进行了C的初步认识,了解了一下基本的概念还学习了包括:命名空间,输入输出以及缺省参数等相关的知识。今天我们将进一步对C入门知识进行学习,主要还需要大家掌握我们接下来要学习的——引用&#xf…

基于SpringCloud的可靠消息最终一致性06:轮询事务消息

上一节把可靠消息最终一致性的正常逻辑代码顺序执行了一次,并且对于同一个事务消息,在正常情况下它要被发送至少两次。 这是因为在发送消息之前,TransactionMessageService就已经把消息保存到了数据库中。而在首次消费完消息后,TransactionMessageListener并没有从数据库中…

冯诺依曼体系结构与操作系统的概念及理解

一、 冯诺依曼体系结构1、概念2、内存的作用3、硬件原理解释软件行为二、操作系统的概念及基本作用1、概念2、设计操作系统的目的3、操作系统的主要作用4、什么是管理5、管理的目的6、操作系统如何为我们服务一、 冯诺依曼体系结构 我们常见的计算机,如笔记本。我们…

只需四步,手把手教你打造专属数字人

伴随ChatGPT的问世,在技术与商业运作上都日渐发展成熟的数字人产业正持续升温。去年9月,北京市发布了国内首个数字人产业专项支持政策,提出将依托国家文化专网将数字人纳入文化数据服务平台。以数字人、ChatGPT为代表的互联网3.0创新应用产业…

【2023】OAK智能深度相机用户实际应用项目(附开源代码)

编辑:OAK中国 首发:oakchina.cn 喜欢的话,请多多👍⭐️✍ 内容可能会不定期更新,官网内容都是最新的,请查看首发地址链接。 ▌前言 Hello,大家好,这里是OAK中国,我是助手…

Linux环境内存管理——分配内存和释放内存

我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重新审视一下Windows程序员如何学习Linux环境内存管理。由于很多程序在Windows环境下开发好后,还要部署到Linux服务器上去,所以作为Windows程序员有必要学习Linux环境的内存…

IntelliJ IDEA 实用插件推荐(包含使用教程)

IntelliJ IDEA 实用插件推荐 背景:电脑重装了,重新下载了最新版的IntelliJ IDEA,感觉默认模式有点枯燥,于是决定从网上下载一些实用美观的插件优化自己以后吃饭的工具,现在推荐的都是目前还能用的(亲身实践…

【java】Java 内存模型

文章目录前言什么是 Java 内存模型为什么需要 Java 内存模型顺序一致性内存模型Happens-Before 规则总结前言 在并发编程中,当多个线程同时访问同一个共享的可变变量时,会产生不确定的结果,所以要编写线程安全的代码,其本质上是对…

C语言青蛙跳台阶【图文详解】

青蛙跳台阶前言1. 题目介绍2. 解题思路3. 利用图片来演示青蛙跳台阶的原理4. 如何用C语言实现青蛙跳台阶前言 在本文,我们要与一只活泼可爱的小青蛙合作,带领着它跳上台阶,这个小家伙精力充沛,特别擅长于跳跃。我们要让它做我们的…

一个诡异的 Pulsar InterruptedException 异常

背景 今天收到业务团队反馈线上有个应用往 Pulsar 中发送消息失败了,经过日志查看得知是发送消息时候抛出了 java.lang.InterruptedException 异常。 和业务沟通后得知是在一个 gRPC 接口中触发的消息发送,大约持续了半个小时的异常后便恢复正常了&…

MySQL数据库中的函数怎样使用?

函数 是指一段可以直接被另一段程序调用的程序或代码。 也就意味着,这一段程序或代码在MySQL中已经给我们提供了,我们要做的就是在合适的业务场景调用对应的函数完成对应的业务需求即可。 那么,函数到底在哪儿使用呢?我们先来看两个场景&…

前端开发:JS的节流与防抖

前言 在前端实际开发中,有关JS原生的节流和防抖处理也是很重要的点,关于底层和原理的掌握使用,尤其是在性能优化方面甚为重要。作为前端开发的进阶内容,在实际开发过程中节流和防抖通常都是项目优化的必要手段,而且也是…

【Project】项目管理软件学习笔记

一、前言使用Project制定项目计划步骤大致如下:以Project2013为例,按照上图步骤指定项目计划。二、实施2.1 创建空白项目点击文件——新建——空白项目,即完成了空白项目的创建,在此我把该项目保存为60mm项目管理.mpp,…

深入浅出1588v2(PTP)里的时间同步原理

1.时间同步1.1 单步同步(OneStep)单步同步最为简单,master向slave发送一个sync的同步包,同步包里带有这条信息发送时master的当前时间t1,假如这条信息从master传输到slave需要的传输时间是D,那么slave收到信息时,maste…

芯驰(E3-gateway)开发板环境搭建

1-Windows下环境配置 可以在Windows上使用命令行或者IAR IDE编译SSDK项目。Windows编译依赖的工具已经包含在 prebuilts/windows 目录中,包括编译器、Python和命令行工具。 1.1.1 CMD SSDK集成 msys 工具,可以在Windows命令行中完成SDK的配置、编译和…

嵌入式系统硬件设计与实践(第一步下载eda软件)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 现实生活中,我们经常发现有的人定了很多的目标,但是到最后一个都没有实现。这听上去有点奇怪,但确实是实实在在…

Mysql数据库总结

一.MySQL 的基础1.架构图Mysql逻辑架构图主要分三层:(1)第一层负责连接处理,授权认证,安全等等 (2)第二层负责编译并优化SQL (3)第三层是存储引擎。Mysql 服务器的默认端…

Welcome to TryHackMe --- 我在TryHackMe学习的第90天

我在TryHackMe学习的第90天 自发的thm玩家交流企鹅群:751273347 TryHackMe是一个及其优秀的道德嗨客学习平台 这三个月里,我在TryHackMe都学了什么 TryHackMe的几个路径我觉得是按照oscp出的,所以理论上讲我应该差不多有oscp水准&#xff…

ElasticSearch修改索引字段类型

一、Es报MapperParsingException异常 线上功能报错,一看日志是往es中添加数据报错,错误日志如下: org.elasticsearch.index.mapper.MapperParsingException: failed to parse field [categoryId] of type [integer] in document with id 16…

软件技术知识库必备的功能清单及注意事项!

文档是一个迭代过程。它可能需要根据客户反馈进行改进,或者可能需要折射文档中已包含的某些内容。知识库可以包括客户的常见问题或对解决方案的更多参考,这些解决方案可能需要包括这些解决方案,以提高效率、生产力并降低公司成本,…