js迭代器与生成器

目录

  • 迭代器
    • next
    • 可迭代对象
    • 自定义类的迭代
    • 迭代器的中断
  • 生成器
    • 生成器函数
    • 返回值与传递参数
    • 生成器的提前结束
    • 生成器的语法糖
    • 使用生成器实现自定义类迭代
  • async和await
    • await

迭代器

迭代器iterator),可以把它当做是一个接口,用户可以使用该接口来遍历数据而不需关心数据内部的实现细节
JavaScript中,迭代器是一个具体的对象
这个对象必须含有一个next方法
拥有迭代器的数据可以被用于for...of展开运算符解构赋值创建对象调用特定方法等等地方

next

next方法是一个无参数或者只有一个参数的函数,应当返回一个拥有done和value两个属性的对象
done属性是一个布尔值,它代表了迭代器是否将数据遍历完成,未完成的话值为false,完成或遍历终止的话值为true
value为迭代器每次在对数据遍历时取得的值donetrue时可以省略,值为undefined 我们可以实现一个数组的迭代器,通过这个迭代器来遍历数组

var arr = [1, 2, 3, 4, 5]
var arrIndex = 0
var arrIterator = {next: function () {if (arrIndex >= arr.length) {return { done: true, value: undefined }} else {return { done: false, value: `arr第${arrIndex}元素是${arr[arrIndex++]}` }}}
}
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())

控制台结果如下

结果

可迭代对象

我们知道在JavaScript对象本身是不可迭代
如果一个对象实现了迭代器时那么这个对象就变成了一个可迭代对象
迭代器的名称必须为@@iterator,在代码中我们可以使用Symbol.iterator访问

var obj = {arr: [1, 2, 3, 4, 5],[Symbol.iterator]() {var index = 0return {next: () => {if (index >= this.arr.length) {return { done: true, value: undefined }} else {return { done: false, value: this.arr[index++] }}}}}
}
var objIterator = obj[Symbol.iterator]()
console.log(objIterator.next())
console.log(objIterator.next())
console.log(objIterator.next())
console.log(objIterator.next())
console.log(objIterator.next())
console.log(objIterator.next())
for (var item of obj) {console.log(item)
}

通过对对象实现迭代器的形式成功让对象可以迭代

自定义类的迭代

我们还可以创建一个类,所有通过这个类new出来的对象都是可迭代对象

class Person {constructor() {this.name = "张三"this.age = "18"}[Symbol.iterator]() {var index = 0;var items = Object.entries(this)return {next: () => {if (index >= items.length) {return { done: true, value: undefined }} else {return { done: false, value: items[index++] }}}}}
}
var p1 = new Person()
for (var item of p1) {console.log(item)
}

控制台结果如下

结果

迭代器的中断

在某些情况下遍历可能会中断
如在遍历的过程中使用了breakreturnthrow等等
我们想要监听中断的话可以添加return方法

class Person {constructor() {this.name = "张三"this.age = "18"}[Symbol.iterator]() {var index = 0;var items = Object.entries(this)return {next: () => {if (index >= items.length) {return { done: true, value: undefined }} else {return { done: false, value: items[index++] }}},return: () => {console.log("监听到了中断操作")return { done: true, value: undefined }}}}
}
var p1 = new Person()
for (var item of p1) {console.log(item)break
}

控制台结果如下
结果

生成器

生成器Generator)是一种新的函数控制解决方案,它能够更加灵活的控制函数什么时候执行,什么时候暂停
生成器本质上是一个特殊的迭代器

生成器函数

生成器函数是一个特殊的函数,生成器函数需要在Function标识符的后面加一个*
生成器函数能通过yield控制函数执行流程
生成器函数会返回一个生成器对象

function* foo() {console.log("aaa")yieldconsole.log("bbb")yieldconsole.log("ccc")yieldconsole.log("ddd")
}
var generator = foo()
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())

控制台结果如下
结果

返回值与传递参数

生成器同样有返回值与传递参数

function* foo(next1) {console.log("aaa", next1)var next2 = yield "return1"console.log("bbb", next2)var next3 = yield "return2"console.log("ccc", next3)var next4 = yield "return3"console.log("ddd", next4)
}
var generator = foo("next1")
console.log(generator.next())
console.log(generator.next("next2"))
console.log(generator.next("next3"))
console.log(generator.next("next4"))
console.log(generator.next("next5"))

控制台结果如下

结果
可以看到在next中传入参数时会被放入yield前接收的变量中
值得注意的是第一次调用next时是不用传递参数的,因为没有yield前面的变量接收参数
如果想要在第一次调用next就传递参数的话需要再foo中传递
每个yield后面都是返回值,这些返回值被存放在value
生成器函数执行完毕后默认的返回值undefined

生成器的提前结束

如果希望生成器提前结束的话可以调用return方法和throw方法

function* foo(next1) {console.log("aaa", next1)var next2 = yield "return1"console.log("bbb", next2)var next3 = yield "return2"console.log("ccc", next3)var next4 = yield "return3"console.log("ddd", next4)
}
var generator = foo("next1")
console.log(generator.next())
console.log(generator.next("next2"))
console.log(generator.return("next3"))
console.log("-------------------------------")
var generator2 = foo("next1")
console.log(generator2.next())
console.log(generator2.next("next2"))
console.log(generator2.throw(new Error("生成器抛出异常")))

控制台结果如下
结果

return方法调用后这个生成器函数就不会再执行了
throw方法则会向生成器函数内部抛出一个异常
生成器函数内部可以使用try catch捕获异常
如果不捕获异常的话生成器函数会停止运行
使用try catch捕获的话在catch内部无法继续yield,在catch外部可以继续函数的执行

生成器的语法糖

如果我们想用生成器来遍历数组的话可以这么写

var arr = [1, 2, 3, 4, 5]
function* foo(arr) {yield* arr
}
var generator = foo(arr)
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())

控制台结果如下
结果
上面这种代码本质上是下面这种代码的语法糖写法

var arr = [1, 2, 3, 4, 5]
function* foo(arr) {for (var i = 0; i < arr.length; i++) {yield arr[i]}
}
var generator = foo(arr)
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())

使用生成器实现自定义类迭代

class Person {constructor() {this.name = "张三"this.age = "18"}*[Symbol.iterator]() {yield* Object.entries(this)}
}
var p1 = new Person()
for (var item of p1) {console.log(item)
}

控制台结果如下
结果
值得注意的是,如果想在类中实现生成器函数,可以在[Symbol.iterator]前加上一个*

async和await

async用于声明一个异步函数
当一个函数前面添加了async关键字时,这个函数就变成了异步函数
异步函数中的代码默认同步执行
异步函数同样有返回值

  1. 返回一个普通值
    这个值会被包裹到Promise.resolve()
  2. 返回一个Promise
    返回一个Promise则状态由Promise决定
  3. 返回一个thenable
    返回一个thenable则状态由then方法决定
    如果在异步函数抛出异常,会作为reject来处理
    异步函数中最大的特点就是可以使用await关键字

await

await关键字通常会跟一个表达式,表达式返回一个Promise
await会等待Promise状态变为fulfilled之后再继续执行代码
await返回的值跟表达式有关

  1. 如果await后面是一个普通的值,那么会直接返回这个值
  2. 如果await后面是一个thenable的对象,那么会根据then方法调用来决定后续的值
  3. 如果await后面的表达式返回的Promise的状态是reject,那么会将这个reject结果直接作为异步函数Promisereject

回到我们最开始的一个问题,我们想要发送一个网络请求并用一个变量接收网络请求的结果
Promise中是这种写法
具体关于Promise可以看我这篇文章
Promise

function request(url) {return new Promise((resolve, reject) => {setTimeout(function () {resolve("success:" + url)}, 1000)})
}
function getRequests() {var datas = []request("http://yes.com").then((res) => {datas.push(res)return request("http://no.com")}).then((res) => {datas.push(res)return request("http://null.com")}).then((res) => {datas.push(res)return request("http://foo.com")}).then((res) => {datas.push(res)})console.log(datas)
}
getRequests()

使用生成器迭代器的写法

function request(url) {return new Promise((resolve, reject) => {setTimeout(function () {resolve("success:" + url)}, 1000)})
}
function* getRequests() {var datas = []var temptemp = yield request("http://yes.com")datas.push(temp)temp = yield request("http://no.com")datas.push(temp)temp = yield request("http://null.com")datas.push(temp)temp = yield request("http://foo.com")datas.push(temp)console.log(datas)
}
var generator = getRequests()
generator.next().value.then((res) => {generator.next(res).value.then((res) => {generator.next(res).value.then((res) => {generator.next(res).value.then((res => {generator.next(res)}))})})
})

而如果使用asyncawait的话这么写

function request(url) {return new Promise((resolve, reject) => {setTimeout(function () {resolve("success:" + url)}, 1000)})
}
async function getRequests() {var datas = []var temptemp = await request("http://yes.com")datas.push(temp)temp = await request("http://no.com")datas.push(temp)temp = await request("http://null.com")datas.push(temp)temp = await request("http://foo.com")datas.push(temp)console.log(datas)
}
getRequests()

这就是异步的最终解决方案

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

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

相关文章

每日一题 25K个一组翻转链表

题目 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内…

比例电磁铁控制放大器

GP63系列比例电磁铁应用于电液比例控制系统中&#xff0c;与比例控制放大器配套使用共同控制力士(REXROTH)型十通径螺纹比例阀。在额定行程及额定电流范围内&#xff0c;其输出力与输入电流成比例&#xff0c;通过内置反力弹簧&#xff0c;改变了输出力的特性&#xff0c;使系统…

读《Flask Web开发实战》(狼书)笔记 | 第1、2章

前言 2023-8-11 以前对网站开发萌生了想法&#xff0c;又有些急于求成&#xff0c;在B站照着视频敲了一个基于flask的博客系统。但对于程序的代码难免有些囫囵吞枣&#xff0c;存在许多模糊或不太理解的地方&#xff0c;只会照葫芦画瓢。 而当自己想开发一个什么网站的时&…

Docker中MySQL应用部署操作步骤

在linux系统下安装mysql、安装redis是非常麻烦的&#xff0c;但是docker出现后&#xff0c;应用安装会非常简洁。 1.MySQL部署 2.docker中部署mysql的步骤 创建mysql容器 这样mysql就部署好了。 外部机器连接docker中部署的mysql

JVM内存区域划分

JVM把虚拟机的内存区域划分为方法区&#xff08;Method Area&#xff09;、堆&#xff08;Heap&#xff09;、栈&#xff08;Java Stack&#xff09;、本地方法栈&#xff08;Native Method Stack&#xff09;、和一个PC寄存器&#xff08;程序计数器&#xff0c;Progam Counti…

FiboSearch Pro – Ajax Search for WooCommerce 商城AJAX实时搜索插件

FiboSearch Pro是最受欢迎的WooCommerce 产品搜索插件。它为您的用户提供精心设计的高级 AJAX 搜索栏&#xff0c;并提供实时搜索建议。默认情况下&#xff0c;WooCommerce 提供非常简单的搜索解决方案&#xff0c;没有实时产品搜索&#xff0c;甚至没有 SKU 搜索。FiboSearch&…

Python爬虫:单线程、多线程、多进程

前言 在使用爬虫爬取数据的时候&#xff0c;当需要爬取的数据量比较大&#xff0c;且急需很快获取到数据的时候&#xff0c;可以考虑将单线程的爬虫写成多线程的爬虫。下面来学习一些它的基础知识和代码编写方法。 一、进程和线程 进程可以理解为是正在运行的程序的实例。进…

尚硅谷大数据项目《在线教育之离线数仓》笔记001

视频地址&#xff1a;尚硅谷大数据项目《在线教育之离线数仓》_哔哩哔哩_bilibili 目录 P003 P004【数仓概念讲的颇为详细】 P018 P019 P020 P021 P022 P023 P024 P003 时间切片&#xff1a;时间回溯&#xff0c;找回以前的数据。 P004【数仓概念讲的颇为详细】 核心架…

VScode如何设置中文教程

前言&#xff1a;打开VSCode软件&#xff0c;可以看到刚刚安装的VSCode软件默认使用的是英文语言环境&#xff0c;但网上都是vscode中文界面教你怎么设置中文&#xff0c;可能不利于小白阅读&#xff0c;所以重装vscode&#xff0c;手摸手从英文变成中文。 设置为中文 打开VS…

释放马氏距离的力量:用 Python 探索多元数据分析

一、说明 马哈拉诺比斯距离&#xff08;Mahalanobis Distance&#xff09;是一种测量两个概率分布之间距离的方法。它是基于样本协方差矩阵的函数&#xff0c;用于评估两个向量之间的相似程度。Mahalanobis Distance考虑了数据集中各个特征之间的协方差&#xff0c;因此比欧氏距…

【833. 字符串中的查找与替换】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 你会得到一个字符串 s (索引从 0 开始)&#xff0c;你必须对它执行 k 个替换操作。替换操作以三个长度均为 k 的并行数组给出&#xff1a;indices, sources, targets。 要完成第 i 个替换操作: 检查…

ES中倒排索引机制

在ES的倒排索引机制中有四个重要的名词&#xff1a;Term、Term Dictionary、Term Index、Posting List。 Term&#xff08;词条&#xff09;&#xff1a;词条是索引里面最小的存储和查询单元。一段文本经过分析器分析以后就会输出一串词条。一般来说英文语境中词条是一个单词&a…