生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
生成器函数也是一个函数,但是和普通的函数有一些区别:
生成器事实上是一种特殊的迭代器
我们在代码中演示一下:
// 定义了一个生成器函数
function* foo() {
console.log("00001")
console.log("00002")
console.log("00003")
console.log("00004")
console.log("00005")
console.log("00006")
}
foo()
// 定义了一个生成器函数
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
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}
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}
函数既然可以暂停来分段执行,那么函数应该是可以传递参数的,我们是否可以给每个分段来传递参数呢?
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"))
还有一个可以给生成器函数传递参数的方法是通过return函数:
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}
除了给生成器函数内部传递参数之外,也可以给生成器函数内部抛出异常:
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"))
我们发现生成器是一种特殊的迭代器,那么在某些情况下我们可以使用生成器来替代迭代器:
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}
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}
生成器案例: 定义一个生成器, 可以创建某个范围的值
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}
在迭代器的时候我们创建过一个自定义类, 用于创建一系列可迭代对象 :
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
}