《每天十分钟》-红宝书第4版-迭代器与生成器(二)

生成器

基础知识

生成器的形式是一个函数,这个函数比较特殊,它拥有在一个函数块内暂停和恢复代码执行的能力。
函数名称前面加一个星号(*) 这就表示它是一个生成器了

// 生成器函数声明
function* generatorFn() {} 
// 生成器函数表达式
let generatorFn = function* () {} 
// 作为对象字面量方法的生成器函数
let foo = { * generatorFn() {} 
} 
// 作为类实例方法的生成器函数
class Foo { * generatorFn() {} 
} 
// 作为类静态方法的生成器函数
class Bar { static * generatorFn() {} 
}

标识生成器函数的星号不受两侧空格的影响

调用生成器函数会产生一个生成器对象。生成器对象一开始处于暂停执行(suspended)的状态。与
迭代器相似,生成器对象也实现了 Iterator 接口,因此具有 next()方法。调用这个方法会让生成器
开始或恢复执行。

function* generatorFn() {} 
const g = generatorFn(); 
console.log(g); // generatorFn {<suspended>} 
console.log(g.next); // f next() { [native code] }
// 函数体为空的生成器函数中间不会停留,调用一次 next()就会让生成器到达 done: true 状态
console.log(g.next()); // { done: true, value: undefined }

value 属性是生成器函数的返回值,默认值为 undefined,可以通过生成器函数的返回值指定:

function* generatorFn() {return 'foo'; 
} 
let generatorObject = generatorFn(); 
console.log(generatorObject); // generatorFn {<suspended>} 
console.log(generatorObject.next()); // { done: true, value: 'foo' }

通过 yield 中断执行

function* generatorFn() { yield 'foo'; yield 'bar'; return 'baz'; 
} 
let generatorObject = generatorFn(); 
let generatorObject2 = generatorFn();
console.log(generatorObject.next()); // { done: false, value: 'foo' } 
console.log(generatorObject.next()); // { done: false, value: 'bar' } 
console.log(generatorObject.next()); // { done: true, value: 'baz' }
//生成器函数内部的执行流程会针对每个生成器对象区分作用域。在一个生成器对象上调用 next()不会影响其他生成器
console.log(generatorObject2.next()); // { done: false, value: 'foo' }

使用场景

在生成器对象上显式调用 next()方法的用处并不大。那使用生成器适用哪些场景

生成器对象作为可迭代对象

function* generatorFn() { yield 1; yield 2; yield 3; 
} 
// 这里就是遍历可迭代对象
for (const x of generatorFn()) { console.log(x); 
} 
// 1 
// 2 
// 3

实现输入输出

//因为函数必须对整个表达式求值才能确定要返回的值,所以它在遇到 yield 关键字时暂停执行并计算出要产生的值:"foo"。下一次调用 next()传入了"bar",作为交给同一个 yield 的值。然后这个值被确定为本次生成器函数要返回的值
function* generatorFn() { return yield 'foo'; 
} 
let generatorObject = generatorFn(); 
console.log(generatorObject.next()); // { done: false, value: 'foo' } 
console.log(generatorObject.next('bar')); // { done: true, value: 'bar' }

yield 关键字并非只能使用一次。比如,以下代码就定义了一个无穷计数生成器函数:

function* generatorFn() { for (let i = 0;;++i) { yield i; } 
} 
let generatorObject = generatorFn(); 
console.log(generatorObject.next().value); // 0 
console.log(generatorObject.next().value); // 1 
console.log(generatorObject.next().value); // 2 
console.log(generatorObject.next().value); // 3 
console.log(generatorObject.next().value); // 4 
console.log(generatorObject.next().value); // 5

有限计数

function* nTimes(n) { for (let i = 0; i < n; ++i) { yield i; } 
} 
for (let x of nTimes(3)) { console.log(x); 
} 
// 0 
// 1 
// 2

产生可迭代对象

使用星号增强 yield

// 等价的 generatorFn: 
// function* generatorFn() { 
// for (const x of [1, 2, 3]) { 
// yield x; 
// } 
// } 
function* generatorFn() { yield* [1, 2, 3]; 
} 
let generatorObject = generatorFn(); 
for (const x of generatorFn()) { console.log(x); 
} 
// 1 
// 2 
// 3

因为 yield*实际上只是将一个可迭代对象序列化为一连串可以单独产出的值,所以这跟把 yield放到一个循环里没什么不同

使用 yield*实现递归算法

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

生成器作为默认迭代器

class Foo { constructor() { this.values = [1, 2, 3]; }* [Symbol.iterator]() { yield* this.values; } 
} 
const f = new Foo(); 
for (const x of f) { console.log(x); 
} 
// 1 
// 2 
// 3

for-of 循环调用了默认迭代器(它恰好又是一个生成器函数)并产生了一个生成器对象。
这个生成器对象是可迭代的,所以完全可以在迭代中使用。

提前终止生成器

与迭代器类似,生成器也支持“可关闭”的概念。一个实现 Iterator 接口的对象一定有 next()方法,还有一个可选的 return()方法用于提前终止迭代器。生成器对象除了有这两个方法,还有第三个方法:throw()。

function* generatorFn() {} 
const g = generatorFn(); 
console.log(g); // generatorFn {<suspended>} 
console.log(g.next); // f next() { [native code] } 
console.log(g.return); // f return() { [native code] } 
console.log(g.throw); // f throw() { [native code] }

小结

迭代是一种所有编程语言中都可以看到的模式。ECMAScript 6 正式支持迭代模式并引入了两个新的语言特性:迭代器和生成器。

迭代器必须通过连续调用 next()方法才能连续取得值,这个方法返回一个 IteratorObject。这个对象包含一个 done 属性和一个 value 属性。前者是一个布尔值,表示是否还有更多值可以访问;后者包含迭代器返回的当前值。这个接口可以通过手动反复调用 next()方法来消费,也可以通过原生消费者,比如 for-of 循环来自动消费。

生成器是一种特殊的函数,调用之后会返回一个生成器对象。生成器对象实现了 Iterable 接口,因此可用在任何消费可迭代对象的地方。生成器的独特之处在于支持 yield 关键字,这个关键字能够暂停执行生成器函数。使用 yield 关键字还可以通过 next()方法接收输入和产生输出。在加上星号之后,yield 关键字可以将跟在它后面的可迭代对象序列化为一连串值。

劝学 唐 孟郊
击石乃有火,不击元无烟。
人学始知道,不学非自然。
万事须己运,他得非我贤。
青春须早为,岂能长少年。

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

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

相关文章

RHCE 部署Ubuntu系统(ubuntu-23.10-live-server-amd64.iso)

目录 一、新建虚拟机 二、安装系统 1、 选择安装语言&#xff0c;默认 【 English 】&#xff0c;直接回车 2、选择键盘&#xff0c;默认回车 3、安装的服务器版本&#xff0c;根据需求自行选择&#xff0c;本次安装选择 【 Ubuntu Server 】 4、网络设置&#xff0c;此…

Unix/Linux上的五种IO模型

a.阻塞 blocking 调用者调用了某个函数&#xff0c;等待这个函数返回&#xff0c;期间什么也不做&#xff0c;不停的去检查这个函数有没有返回&#xff0c;必须等这个函数返回才能进行下一步动作。 注意&#xff1a;阻塞并不是函数的行为&#xff0c;而是跟文件描述符有关。通…

力扣题目训练(5)

2024年1月29日力扣题目训练 2024年1月29日力扣题目训练345. 反转字符串中的元音字母349. 两个数组的交集350. 两个数组的交集 II96. 不同的二叉搜索树97. 交错字符串44. 通配符匹配 2024年1月29日力扣题目训练 2024年1月29日第五天编程训练&#xff0c;今天主要是进行一些题训…

【云上建站】快速在云上构建个人网站3——网站选型和搭建

快速在云上构建个人网站3——网站选型和搭建 一、网站选型二、云市场镜像方式一&#xff1a;方式二&#xff1a;1. 进入ECS实例详情页面&#xff0c;点击停止&#xff0c;确保更换操作系统的之前ECS实例处于已停止状态&#xff0c;点击更换操作系统&#xff0c;进行镜像配置。2…

如何写好论文——(5)文献综述的两个基本点

写论文的要点就是要明确我们的研究目标 在我们的引言中要论证我们研究目标的合理性 论证通常要通过文献综述来完成 做好文献综述的两个基本要点如下&#xff1a; 一、有所学 在文献综述中展示与我们现有研究相关的所有知识&#xff0c;有一个全面的了解 包括和我们研究问…

84、介绍:操作系统中内存申请的性能

本节简单的介绍一下在操作系统中的内存申请机制。 有些同学看到这可能会有疑惑,我们不是在学习 AI吗,为什么要介绍内存申请的知识呢? 因为无论是 AI,还是其他的计算学科,都离不开以下几个方面的内容:算法 + 操作系统 + 芯片 + 数据。 AI 作为这几年爆火的学科,不是突…

【LeetCode-435】无重叠区间(贪心)

题目链接 题目简介 给定一个区间的集合&#xff0c;找到需要移除区间的最小数量&#xff0c;使剩余区间互不重叠。 注意: 可以认为区间的终点总是大于它的起点。 区间 [1,2] 和 [2,3] 的边界相互“接触”&#xff0c;但没有相互重叠。 示例 1: 输入: [ [1,2], [2,3], [3,4…

深入解析可解释性人工智能(XAI):拓展智能决策的透明边界

引言&#xff1a; 近年来&#xff0c;人工智能技术的飞速发展引发了社会各界对其应用和影响的关注。随着AI系统在日常生活中的广泛应用&#xff0c;人们对于这些系统的决策过程提出了更多的疑问。为了增强对AI系统的信任&#xff0c;科学家们努力推动可解释性AI&#xff08;XAI…

appsmith安装手记:4.Sql server数据库容器安装

appsmith安装好&#xff0c;那就可以看是练练手。 数据当然是来自数据库&#xff0c;那就连接局域网中现成的一台数据库服务器试试&#xff0c;但是连接数据库的时候一直错误。 找到/home/appsmith/backend 目录下的日志&#xff0c;看到了错误&#xff1a; [rootlocalhost bac…

uniapp如何引入uview组件?

目录 1.引入前准备 2.在项目中引入组件 1.mian.is文件 2.uni.scss 文件 3.App.vue文件 4.pages.json 文件 3.测试成功 1.引入前准备 为了方便我们在制作项目的过程中&#xff0c;方便使用模板组件&#xff0c;快速开发。我们可以选择引入组件。 在uni-app中使用uView组件…

day22打卡

day22打卡 235. 二叉搜索树的最近公共祖先 递归法时间复杂度&#xff1a;O(N)&#xff0c;空间复杂度&#xff1a;O(N) class Solution { public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if(root->val > p->val && ro…

蓝桥杯《剪格子》

题目描述 历届试题 剪格子 时间限制&#xff1a;1.0s 内存限制&#xff1a;256.0MB 问题描述 如下图所示&#xff0c;3 x 3 的格子中填写了一些整数。 10 1 52 20 30 1 1 2 3 我们沿着图中的星号线剪开&#xff0c;得到两个部分&#xff0c;每个部分…

基于SSM的高校班级同学录网站设计与实现(有报告)。Javaee项目,ssm项目。

演示视频&#xff1a; 基于SSM的高校班级同学录网站设计与实现&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm项目。 项目介绍&#xff1a; Javaee项目&#xff0c;采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&…

双非本科准备秋招(12.2)—— 力扣栈与队列

复习一下栈和队列的基础知识&#xff0c;刷几道题上上手。 1、102. 二叉树的层序遍历 广度优先遍历嘛&#xff0c;每次拓展一个新结点&#xff0c;就把新结点加入队列&#xff0c;这样遍历完队列中的元素&#xff0c;顺序就是层序遍历。 class Solution {public List<Lis…

我国个体工商户达1.24亿户,支撑近3亿人就业

官媒报道截至2023年底&#xff0c;全国登记在册个体工商户1.24亿户&#xff0c;占经营主体总量的67.4%&#xff0c;支撑近3亿人就业。 什么概念&#xff1f;我帮大家理解一下&#xff0c;2024年了&#xff0c;现在中国5个人里面就有一个人不用上班&#xff0c;而是自己当起了老…

Linux 内核学习1. 编译并启动一个最小化系统

Linux 内核学习1. 编译并启动一个最小化系统 一、Linux内核简介1. Linux 内核介绍2. Linux内核主要的作用 二、编译内核主要的步骤三、编译过程1. 准备环境2. 安装编译工具和依赖项3. 下载源码4. 配置内核配置功能选项命令行配置图形化配置默认配置 5. 编译内核6. 构建轻量化工…

C语言-算法-树状数组

统计和 题目描述 给定一个长度为 n ( n ≤ 100000 ) n(n\leq 100000) n(n≤100000)&#xff0c;初始值都为 0 0 0 的序列&#xff0c; x ( x ≤ 100000 ) x(x\leq 100000) x(x≤100000) 次的修改某些位置上的数字&#xff0c;每次加上一个数&#xff0c;然后提出 y ( y ≤…

mysql 允许其他ip访问

1.改表法。 可能是你的帐号不允许从远程登陆&#xff0c;只能在localhost。这个时候只要在localhost的那台电脑&#xff0c;登入mysql后&#xff0c;更改 “mysql” 数据库里的 “user” 表里的 “host” 项&#xff0c;从"localhost"改称"%" //执行以下…

#翻转牛群

题目描述 农夫约翰偶尔会遇到无聊的青少年&#xff0c;他们晚上去他的农场&#xff0c;把奶牛们弄翻。一天早上&#xff0c;他醒来发现事情又发生了——他的 N∗N 头奶牛排列成了一个完美的 N∗N 方阵&#xff08;1≤N≤10)&#xff0c;但他发现其中一些现在已经被弄翻了&…

学习使用Flask模拟接口进行测试

前言 学习使用一个新工具&#xff0c;首先找一段代码学习一下&#xff0c;基本掌握用法&#xff0c;然后再考虑每一部分是做什么的 Flask的初始化 app Flask(__name__)&#xff1a;初始化&#xff0c;创建一个该类的实例&#xff0c;第一个参数是应用模块或者包的名称 app…