Generator函数(生成器和迭代器)
1. 背景
在 ES6 之前,JavaScript 中的异步编程主要依靠回调函数来处理。回调函数会在异步操作完成后被调用,但它们通常会导致回调地狱(callback hell),使得代码难以理解和维护。为了改善这种情况,ES6 引入了 Promise 对象,提供了更直观的异步处理方式。
然而,Promise 对象仍然需要通过链式调用的方式来处理连续的异步操作,有时候仍然显得复杂。这时候 Generator 函数就派上用场了。
Generator 函数是一种特殊的函数,它可以通过 yield 关键字将执行过程分割成多个部分,每次只执行一部分代码,然后暂停并返回一个中间结果(iterator 对象)。随后,我们可以通过调用 iterator 对象的
next()
方法来恢复函数的执行,并继续执行下一部分代码。
2. 定义
Generator 函数使用
function*
关键字定义,通过yield
关键字将函数的执行过程分割成多个部分,并返回一个 iterator 对象。通过调用 iterator 对象的next()
方法来控制函数的执行。
3. 例子
3.1 generator函数的基本用法
function* fn (str) {
let a = yield str // yield表达式只能用在 Generator 函数里面,用在其他地方都会报错。
yield a
}
const res = fn(1) // Object [Generator] {} 返回一个指向内部状态的指针对象。
const res1 = res.next(); // { value: 1, done: false }
// 必须调用遍历器对象的next方法,使得指针移向下一个状态。
const res2 = res.next(2); // { value: 2, done: false } 这一步开始可以传值 => a = 2
const res3 = res.next() // { value: undefined, done: true }
3.2 generator函数中执行另一个generator函数
yield*: 用于委托(delegating)给另一个可迭代对象(iterable)或 Generator 函数。
当在 Generator 函数内部使用
yield*
表达式时,它会暂停当前 Generator 函数的执行,并将控制权转移到被委托的可迭代对象或 Generator 函数上。这样可以使得 Generator 函数能够递归地遍历或执行其他可迭代对象或 Generator 函数的值。
function* fn1 () {
yield 1
yield 2
yield 3
}
function* fn2() {
yield 4
yield* fn1()// yield* fn1() 等价于 => for (let v of fn1())
yield 5
}
for (let v of fn2()) {
console.log(v)
}
3.3 this问题
generator 函数不是构造函数
function* Person () {
yield this.name = 'zhangsan'
yield this.age = 18
}
// let person = new Person(); // 报错!Person is not a constructor
let haha = {};
let res = Person.bind(haha)();
res.next(); // { value: 'zhangsan', done: false }
res.next() // { value: 18, done: false }
console.log(haha) // { name: 'zhangsan', age: 18 }
4. 面试题
4.1 关于传参
function* test (num) {
let x = 3 * (yield num + 1);
let y = yield x / 3;
return x + y + num;
}
let n = test(6);
// let res1 = n.next(); // 7
// let res2 = n.next(); // NaN => 没有传参数,x是undefined
// let res3 = n.next(); // NaN
let res1 = n.next(); // 7
let res2 = n.next(3); // x = 3 * 3 = 9
let res3 = n.next(3); // y = 3; 3 + 9 + 6
4.2 实现for...of
const each = (val) => {
let It = val[Symbol.iterator](); // 迭代器
let next = { done: false };
while (!next.done) {
next = It.next();
if (!next.done) {
console.log(next.value);
}
}
};
each([1, 2, 3]); // 1,2,3
// each({ a: 1, b: 2, c: 3 }) // TypeError: val[Symbol.iterator] is not a function => 对象身上没有iterator
其他
解构
// 5. 解构 / ... 的底层原理 -- 调用iterator; 注意,对象的解构,原理是普通对象解构赋值的内部机制,是先for in根据可枚举属性找到同名属性,然后再赋给对应的变量
let [a1, b1] = [1, 2];
let a2 = [1, 2];
let b2 = [...a2];
对象的解构:是基于对象的属性访问机制。当访问对象的属性时,可以使用点操作符(
.
)或方括号操作符([]
)来访问属性的值。在对象解构赋值时,JavaScript 引擎会自动根据解构模式中的属性名称,使用对应的属性访问机制来获取属性的值,并将其赋值给对应的变量。
总结
本文是对面试中问到generator的总结,感谢阅读。
转载自:https://juejin.cn/post/7377191678862278693