likes
comments
collection
share

Generator函数(生成器和迭代器)

作者站长头像
站长
· 阅读数 49

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
评论
请登录