文章目录
1.生成器介绍
1.1 什么是生成器
生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
- 平时我们会编写很多的函数,这些函数终止的条件通常是返回值或者发生了异常。
生成器函数也是一个函数,但是和普通的函数有一些区别:
- 首先,生成器函数需要在function的后面加一个符号:*
- 其次,生成器函数可以通过yield关键字来控制函数的执行流程
- 最后,生成器函数的返回值是一个Generator(生成器)
生成器事实上是一种特殊的迭代器
1.2 生成器函数执行
我们在代码中演示一下:
- 生成器需要在function后面添加一个符号 *
// 定义了一个生成器函数
function* foo() {
console.log("00001")
console.log("00002")
console.log("00003")
console.log("00004")
console.log("00005")
console.log("00006")
}
foo()
- 生成器代码的执行可以被yield控制
// 定义了一个生成器函数
function* foo() {
console.log("00001")
console.log("00002")
yield
console.log("00003")
console.log("00004")
yield
console.log("00005")
console.log("00006")
}
foo()
-
生成器函数默认在执行时, 返回的也是一个生成器对象, 我们发现上面代码中, 调用函数时函数内部的代码并没有执行
如果想要执行函数内部的代码, 需要调用返回的生成器对象的next方法
当函数内部代码, 遇到yield时会中断执行
// 1.定义了一个生成器函数
function* foo() {
console.log("00001")
console.log("00002")
yield console.log("aaaaa")
console.log("00003")
console.log("00004")
yield
console.log("00005")
console.log("00006")
}
// 2.调用生成器函数, 会返回一个生成器对象
const generator = foo()
// 当遇到yield时 会执行到与yield右边的代码后中断 yield左边如果有代码不执行 例如上面第一个yield后面还有代码也会执行
generator.next() // 00001 00002 aaaaa
generator.next() // 00003 00004
generator.next() // 00005 00006
- 我们之前学习迭代器时,知道迭代器的next是会有返回值的, 而生成器也是一个特殊的迭代器, 那么我们看一下生成器调用next方法会返回什么
function* foo() {
console.log("00001")
console.log("00002")
yield
console.log("00003")
console.log("00004")
yield
console.log("00005")
console.log("00006")
}
// 生成器返回一个生成器
const generator = foo()
console.log(generator.next()) // {value: undefined, done: false}
console.log(generator.next()) // {value: undefined, done: false}
console.log(generator.next()) // {value: undefined, done: true}
- 但是我们很多时候不希望next返回的是一个undefined,这个时候我们可以通过yield来返回结果;
function* foo() {
console.log("00001")
console.log("00002")
yield "aaaaa"
console.log("00003")
console.log("00004")
yield "bbbbb"
console.log("00005")
console.log("00006")
}
// 生成器返回一个生成器
const generator = foo()
console.log(generator.next()) // {value: "aaaaa", done: false}
console.log(generator.next()) // {value: "bbbbb", done: false}
console.log(generator.next()) // {value: undefined, done: true}
1.3 生成器传递参数
函数既然可以暂停来分段执行,那么函数应该是可以传递参数的,我们是否可以给每个分段来传递参数呢?
- 答案是可以的, 我们在调用next函数的时候,可以给它传递参数,那么这个参数会作为上一个yield语句的返回值;
function* foo(name1) {
console.log("00001", name1)
console.log("00002", name1)
const name2 = yield "aaaaa"
console.log("00003", name2)
console.log("00004", name2)
const name3 = yield "bbbbb"
console.log("00005", name3)
console.log("00006", name3)
}
// 生成器返回一个生成器
const generator = foo("name1")
// 第一次传递参数是通过函数传递
console.log(generator.next("name1"))
// 第二次及后面开始传递参数, 是通过yield
console.log(generator.next("name2"))
console.log(generator.next("name3"))
1.4 生成器提前结束
还有一个可以给生成器函数传递参数的方法是通过return函数:
- return传值后这个生成器函数就会结束,之后调用next不会继续生成值了;
function* foo(name1) {
console.log("00001", name1)
console.log("00002", name1)
const name2 = yield "aaaaa"
console.log("00003", name2)
console.log("00004", name2)
const name3 = yield "bbbbb"
console.log("00005", name3)
console.log("00006", name3)
}
// 生成器返回一个生成器
const generator = foo("name1")
console.log(generator.next("name1")) // {value: 'aaaaa', done: false}
// 通过return提前结束生成器
console.log(generator.return("name2")) // {value: 'name2', done: true}
// return之后再通过next调用不会继续生成值
console.log(generator.next("name3")) // {value: undefined, done: true}
console.log(generator.next()) // {value: undefined, done: true}
console.log(generator.next()) // {value: undefined, done: true}
1.5 生成器抛出异常
除了给生成器函数内部传递参数之外,也可以给生成器函数内部抛出异常:
- 抛出异常后我们可以在生成器函数中捕获异常;
- 但是在catch语句中不能继续yield新的值了,但是可以在catch语句外使用yield继续中断函数的执行;
- 目前未学习捕获异常语句, 了解即可
function* foo(name1) {
console.log("00001", name1)
console.log("00002", name1)
const name2 = yield "aaaaa"
console.log("00003", name2)
console.log("00004", name2)
const name3 = yield "bbbbb"
console.log("00005", name3)
console.log("00006", name3)
}
// 生成器返回一个生成器
const generator = foo("name1")
console.log(generator.next("name1")) // {value: 'aaaaa', done: false}
console.log(generator.throw("name2 throw")) // Uncaught name2 throw
console.log(generator.return("name2"))
console.log(generator.next("name3"))
2.生成器应用
2.1 生成器替代迭代器
我们发现生成器是一种特殊的迭代器,那么在某些情况下我们可以使用生成器来替代迭代器:
- 我们前面学习迭代器的时候, 有写过下面这样一个案例用于实现一个迭代器
function createArrayIterator(arr) {
let index = 0
return {
next: function() {
if (index < arr.length) {
return { done: false, value: arr[index++] }
} else {
return { done: true }
}
}
}
}
- 我们可以利用生成器替代迭代器进行优化
const names = ["aaa", "bbb", "ccc"]
function* createArrayIterator(arr) {
for (let i = 0; i < arr.length; i++) {
yield arr[i]
}
}
const namesIterator = createArrayIterator(names)
console.log(namesIterator.next()) // {value: 'aaa', done: false}
console.log(namesIterator.next()) // {value: 'bbb', done: false}
console.log(namesIterator.next()) // {value: 'ccc', done: false}
console.log(namesIterator.next()) // {value: undefined, done: true}
- 事实上我们还可以使用yield*来生产一个可迭代对象:这个时候相当于是一种yield的语法糖,只不过会依次迭代这个可迭代对象,每次迭代其中的一个值;
const names = ["aaa", "bbb", "ccc"]
function* createArrayIterator(arr) {
yield* arr
}
const namesIterator = createArrayIterator(names)
console.log(namesIterator.next()) // {value: 'aaa', done: false}
console.log(namesIterator.next()) // {value: 'bbb', done: false}
console.log(namesIterator.next()) // {value: 'ccc', done: false}
console.log(namesIterator.next()) // {value: undefined, done: true}
生成器案例: 定义一个生成器, 可以创建某个范围的值
- 例如: 生成 [3, 9) 之间的值
function* createRangeGenerator(start, end) {
for (let i = start; i < end; i++) {
yield i
}
}
const numsGenerator = createRangeGenerator(3, 9)
console.log(numsGenerator.next()) // {value: 3, done: false}
console.log(numsGenerator.next()) // {value: 4, done: false}
console.log(numsGenerator.next()) // {value: 5, done: false}
console.log(numsGenerator.next()) // {value: 6, done: false}
console.log(numsGenerator.next()) // {value: 7, done: false}
console.log(numsGenerator.next()) // {value: 8, done: false}
console.log(numsGenerator.next()) // {value: undefined, done: true}
2.2 生成器实现自定义类迭代
在迭代器的时候我们创建过一个自定义类, 用于创建一系列可迭代对象 :
class Person {
constructor(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
}
// 添加实例方法, 迭代器
[Symbol.iterator] () {
let index = 0
return {
next: () => {
if (index < this.friends.length) {
return { done: false, value: this.friends[index++] }
} else {
return { done: true }
}
}
}
}
}
// 这样Person类创建出来的对象都是可迭代对象
const p1 = new Person("kaisa", 18, ["aaa", "bbb", "ccc"])
// 可以进行for...of操作
for (item of p1) {
console.log(item) // aaa bbb ccc
}
这个自定义类我们也可以换成生成器, 对上面代码优化:
class Person {
constructor(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
}
// 添加实例方法, 迭代器前面加上*变为生成器
*[Symbol.iterator] () {
// yield*后面跟要迭代的对象
yield* this.friends
}
}
// 进行for...of操作
const p1 = new Person("kaisa", 18, ["aaa", "bbb", "ccc"])
for (item of p1) {
console.log(item) // aaa bbb ccc
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/120129.html