什么是生成器
生成器是一个通过构造函数Generator创建的对象,生成器既是一个迭代器,同时又是一个可迭代对象
如图中 函数表达式c的隐士原型为 Generator 且隐士原型中还拥有next函数,next函数为迭代器,同时隐士原型中还拥有符号属性Symbol.iterator 该函数为可迭代对象
生成器的出现是为啦更加方便的书写迭代器
示例
const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 迭代器创建函数 iterator creator
function createIterator(arr) {
let i = 0;//当前的数组下标
return {
next() {
var result = {
value: arr[i],
done: i >= arr.length
}
i++;
return result;
}
}
}
const iter1 = createIterator(arr1);
当使用生成器后
const arr = [2, 3, 5, 4, 9]
function* arrIterater() {
for (const iterator of arr) {
yield iterator;
}
}
const a = arrIterater()
可以直接发现,每次写迭代器就很繁琐 , 尤其是书写可迭代对象,可迭代对象的格式看迭代器的笔记
如何创建与书写生成器
生成器的创建,必须使用生成器函数(Generator Function)
书写生成器的的格式
//这是一个生成器函数,该函数一定返回一个生成器
function* method(){
yield "产生一个迭代数据"
}
生成器的内部如何执行
生成器函数内部是为了给生成器的每次迭代提供的数据
每次调用生成器的next方法,将导致生成器函数运行到下一个yield关键字位置
yield是一个关键字,该关键字只能在生成器函数内部使用,表达“产生”一个迭代数据。
需注意的细节
1). 生成器函数可以有返回值,返回值出现在第一次done为true时的value属性中
2). 调用生成器的next方法时,可以传递参数,传递的参数会交给yield表达式的返回值
3). 第一次调用next方法时,传参没有任何意义
4). 在生成器函数内部,可以调用其他生成器函数,但是要注意加上*号
示例
function* test() {
console.log("第1次运行")
yield 1;
console.log("第2次运行")
yield 2;
console.log("第3次运行")
}
const generator = test();
其在控制台中的执行效果
生成器函数可以有返回值,返回值出现在第一次done为true时的value属性中
function* Generator() {
console.log("第1次运行")
yield 1;
console.log("第2次运行")
yield 2 ;
console.log("第3次运行")
return "生成器函数可以有返回值,返回值出现在第一次done为true时的value属性中"
}
const g = Generator()
从控制台的打印效果可以看出,在第三次迭代数据中返回的值为规定结束时返回的值,但是当第四次迭代数据的返回值却为unfinished 这说明返回值值出现在第一次done为true时的value属性中
调用生成器的next方法时,可以传递参数,传递的参数会交给yield表达式的返回值 / 第一次调用next方法时,传参没有任何意义
function* Generator() {
console.log("第1次运行")
let info = yield 1;
console.log(info ,"-info的值")
console.log("第2次运行")
yield 2 + info;
console.log(info,"-info的值")
console.log("第3次运行")
}
const g = Generator()
如上面代码中 第二次迭代数据中有为未知的数据(info),当往里传入参数时,会发生何种效果
如上图的控制打印效果,传入参数 第一次迭代的数据没有任何效果,到啦第二次就有效果啦这是为什么
内部执行原理
g.next(50) 第一次迭代效果 ,传递的参数会交给yield表达式的返回值 ,那就是说 先执行yield 1;此时let info = yield 1;这句话只执行啦一半并还没有给info赋值,然后到第二次迭代传递的参数会交给yield表达式的返回值,那么 yield 1;的返回值就会是50,且赋值给info,这也就是第二次赋值结果为啥是52 这也说明 第一次调用next方法时,传参没有任何意义
在生成器函数内部,可以调用其他生成器函数,但是要注意加上*号
function* t1(){
yield "a"
yield "b"
}
function* test() {
yield* t1();
yield 1;
yield 2;
yield 3;
}
const generator = test();
从控制台执行效果可以看到先执行生成器函数t1()等到t1()执行完才执行的text()
生成器的其他api
return方法:调用该方法,可以提前结束生成器函数,从而提前让整个迭代过程结束
- throw方法:调用该方法,可以在生成器中产生一个错误function* Generator() {
console.log("第1次运行")
yield 1;
console.log("第2次运行")
yield 2;
console.log("第3次运行")
}
const g = Generator()
如图 在其控制台的打印效果,第二次的迭代中使用return 方法将其提前结束生成器函数,之后继续调动next方法返回都是unfinished的值
如上图,第一次正常迭代数据,第二次使用throw方法爆出错误,这个错误是故意爆出的错误,其代码并没有错误,之后继续调用next方法与return的返回数据结果一样案例
// 斐波拉契数列
// 1 1 2 3 5 8 13
function* createFeiBoLaQiIterater() {
let prev1 = 1,
prev2 = 1;
let n = 1;
while (true) {
if (n <= 2) {
yield 1
} else {
let newPrev = prev1 + prev2
yield newPrev;
prev2 = prev1;
prev1 = newPrev;
}
n++
}
}
const c = createFeiBoLaQiIterater()