Generator之详细讲解及用法 附有源码!!!-前端-E先生的博客
Java
MySQL
大数据
Python
前端
黑科技
大语言模型
    首页 >> 互联网 >> 前端

Generator之详细讲解及用法 附有源码!!!

[导读]:Generator是ES6提出的一种异步编程的方案。因为手动创建一个iterator十分麻烦,因此ES6推出了generator,用于更方便的创建iterator。也就是说,Generator就是一个返回值为iterator对象的函数。在讲Generator之前,我们先来看看iterator是什么:iterator是什么?iterator中文名叫迭代器。它为js中各种不同的数据结构(Object、...

Generator是ES6提出的一种异步编程的方案。因为手动创建一个iterator十分麻烦,因此ES6推出了generator,用于更方便的创建iterator。也就是说,Generator就是一个返回值为iterator对象的函数。

在讲Generator之前,我们先来看看iterator是什么:

iterator是什么?

iterator中文名叫迭代器。它为js中各种不同的数据结构(Object、Array、Set、Map)提供统一的访问机制。任何数据结构只要部署了Iterator接口,就可以完成遍历操作。 因此iterator也是一种对象,不过相比于普通对象来说,它有着专为迭代而设计的接口。

iterator 的作用:

    为各种数据结构,提供一个统一的、简便的访问接口;

    使得数据结构的成员能够按某种次序排列;

    ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费

iterator的结构: 它有next方法,该方法返回一个包含value和done两个属性的对象(我们假设叫result)。value是迭代的值,后者是表明迭代是否完成的标志。true表示迭代完成,false表示没有。iterator内部有指向迭代位置的指针,每次调用next,自动移动指针并返回相应的result。

原生具备iterator接口的数据结构如下:

  • Array

  • Map

  • Set

  • String

  • TypedArray

  • 函数里的arguments对象

  • NodeList对象

这些数据结构都有一个Symbol.iterator属性,可以直接通过这个属性来直接创建一个迭代器。也就是说,Symbol.iterator属性只是一个用来创建迭代器的接口,而不是一个迭代器,因为它不含遍历的部分。

使用Symbol.iterator接口生成iterator迭代器来遍历数组的过程为:

let arr = ['a','b','c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

for ... of的循环内部实现机制其实就是iterator,它首先调用被遍历集合对象的 Symbol.iterator 方法,该方法返回一个迭代器对象,迭代器对象是可以拥有.next()方法的任何对象,然后,在 for ... of 的每次循环中,都将调用该迭代器对象上的 .next 方法。然后使用for i of打印出来的i也就是调用.next方法后得到的对象上的value属性。

对于原生不具备iterator接口的数据结构,比如Object,我们可以采用自定义的方式来创建一个遍历器。

比如,我们可以自定义一个iterator来遍历对象:

let obj = {a: hello, b: world};
// 自定义迭代器
function createIterator(items) {
    let keyArr = Object.keys(items);
    let i = 0;
    return {
        next: function () {
            let done = (i >= keyArr.length);
            let value = !done ? items[keyArr[i++]] : undefined;
            return {
                value: value,
                done: done,
            };
        }
    };
}
let iterator = createIterator(obj);
console.log(iterator.next()); // { value: 'hello', done: false }
console.log(iterator.next());  // { value: 'world', done: false }
console.log(iterator.next());  // { value: undefined, done: true }

接下来,我们来聊聊Generator:

我们通过一个例子来看看Gnerator的特征:

function* createIterator() {
  yield 1;
  yield 2;
  yield 3;
}
// generators可以像正常函数一样被调用,不同的是会返回一个 iterator
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3

Generator 函数是 ES6 提供的一种异步编程解决方案。形式上,Generator 函数是一个普通函数,但是有两个特征:

function关键字与函数名之间有一个星号

函数体内部使用yield语句,定义不同的内部状态

Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)

打印看看Generator函数返回值的内容:

发现generator函数的返回值的原型链上确实有iterator对象该有的next,这充分说明了generator的返回值是一个iterator。除此之外还有函数该有的return方法和throw方法。

在普通函数中,我们想要一个函数最终的执行结果,一般都是return出来,或者以return作为结束函数的标准。运行函数时也不能被打断,期间也不能从外部再传入值到函数体内。但在generator中,就打破了这几点,所以generator和普通的函数完全不同。当以function*的方式声明了一个Generator生成器时,内部是可以有许多状态的,以yield进行断点间隔。期间我们执行调用这个生成的Generator,他会返回一个遍历器对象,用这个对象上的方法,实现获得一个yield后面输出的结果。

function* generator() {
    yield 1
    yield 2
};
let iterator = generator();
iterator.next()  // {value: 1, done: false}
iterator.next()  // {value: 2, done: false}
iterator.next()  // {value: undefined, done: true}

yield和return的区别:

    都能返回紧跟在语句后面的那个表达式的值

    yield相比于return来说,更像是一个断点。遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。

    一个函数里面,只能执行一个return语句,但是可以执行多次yield表达式。

    正常函数只能返回一个值,因为只能执行一次return;Generator 函数可以返回一系列的值,因为可以有任意多个yield

语法注意点:

yield表达式只能用在 Generator 函数里面

yield表达式如果用在另一个表达式之中,必须放在圆括号里面

yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号。

如果 return 语句后面还有 yield 表达式,那么后面的 yield 完全不生效

使用Generator的其余注意事项:

需要注意的是,yield 不能跨函数。并且yield需要和*配套使用,别处使用无效

function* createIterator(items) {
  items.forEach(function (item) {
    // 语法错误
    yield item + 1;
  });
}

箭头函数不能用做 generator

讲了这么多,那么Generator到底有什么用呢?

因为Generator可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator就可以实现需要用面向对象才能实现的功能。

Generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。这个在ajax请求中很有用,避免了回调地狱.

image.png


本文来自E先生的博客,如若转载,请注明出处:https://www.javajz.cn

留言区

联系人:
手   机:
内   容:
验证码:

历史留言

欢迎加Easy的QQ