ES6+ Generator 基础介绍
发布时间:2023-03-28 08:50:31 所属栏目:教程 来源:
导读:Generator 就是我们说的生成器,它包含两个概念 生成器对象和生成器函数。首先,要理解的是生成器对象和迭代器的关系,生成器对象是执行迭代协议的迭代器接口,它遵守迭代协议和迭代器协议实施,可以理解生成器对象其
Generator 就是我们说的生成器,它包含两个概念 生成器对象和生成器函数。首先,要理解的是生成器对象和迭代器的关系,生成器对象是执行迭代协议的迭代器接口,它遵守迭代协议和迭代器协议实施,可以理解生成器对象其实也是一个迭代器;然后,我们需要理解什么是生成器函数,生成器函数是由 function * 来定义的,并且返回结果是一个 Generator 对象。 生成器是一个特殊的函数,在调用后会返回一个生成器对象,这个生成器对象是遵守可迭代协议和迭代器协议实现的 Iterable 接口。生成器可以使用 yield 关键字来暂停执行的生成器函数: function* generator() { yield 'a'; yield 'b'; } var gen = generator(); // Object [Generator] {} 生成器的 next () 方法和迭代器返回的结果是一样的,返回了一个包含属性 done 和 value 的对象,该方法也可以通过接受一个参数用以向生成器传值。 使用 yield 返回的值会被迭代器的 next () 方法捕获: var gen = generator(); gen.next() // {value: 'a', done: false} gen.next() // {value: 'b', done: false} gen.next() // {value: undefined, done: true} 从上面代码的执行结果可以看出,生成器函数在执行后会返回一个生成器对象,这个生成器对象满足迭代协议和迭代器协议,所以我们可以去手动调用它的 next () 方法去获取每一步的返回值。从这里可以看出,生成器其实就是迭代器的一个应用,并且这个应用会在异步中大放异彩。 return() 方法返回给定的值并结束生成器。 var gen = generator(); gen.next(); // { value: 'a', done: false } gen.return("imooc"); // { value: "imooc", done: true } gen.next(); // { value: undefined, done: true } 另外,如果对已经完成状态的生成器调用 return(value) 则生成器会一直保持在完成状态,如果出入参数,value 会设置成传入的参数,done 的值不变: var gen = generator(); gen.next(); // { value: 1, done: false } gen.next(); // { value: 2, done: false } gen.next(); // { value: undefined, done: true } gen.return(); // { value: undefined, done: true } gen.return(); // { value: 1, done: true } throw() 方法用来向生成器抛出异常,并恢复生成器的执行,返回带有 done 及 value 两个属性的对象。 function* generator() { while(true) { try { yield 'imooc' } catch(e) { console.log("Error caught!"); } } } var gen = generator(); gen.next(); // { value: "imooc", done: false } gen.throw(new Error("error")); // "Error caught!" 将一个类数组转化为一个真正的数组方式有很多,ES6 提供了 Array.from() 可以将类数组转化为数组 。另外在一些函数中可以使用 [...argument] 的方式转化类数组。 function fn() { const arg = [...arguments]; console.log(arg); } fn(, , ); // [1, 2, 3] 当然我们知道类数组的定义,所以我们自己定义一个类数组,看能不能使用展开运算符将类数组转化为数组: const likeArr = { : , : , length: , } console.log([...likeArr]); // Uncaught TypeError: likeArr is not iterable 上面代码中我们定义了一个类数组,但是使用展开运算符报错了,提示我们 likeArr 不是一个迭代器。因为在函数中类数组是内部帮我们实现了迭代器的功能,而我们自己定义的类数组是不具有迭代器功能的,那我们来自己实现一个: likeArr[Symbol.iterator] = function() { let index = ; return { next: () => { return { value: this[index], done: index++ === this.length} } } } console.log([...likeArr]); // [1, 2] 上面的代码我们在 likeArr 对象上定义了 Symbol.iterator 它具有迭代功能。上面代码中我们需要手动地去实现 next () 方法,这比较麻烦,那能不能简化一下呢?我们的生成器函数就出场了: likeArr[Symbol.iterator] = function* () { let index = ; while (index !== this.length) { yield this[index++]; } } console.log([...likeArr]); // [1, 2] 上面的代码使用了生成器函数,并且没有去手动实现 next () 方法,从这里我们也能很清楚地知道迭代器和生成器的关系。而且使用生成器函数更加简单方便。 还有一个案例是面试中经常会考到的: 题目:实现一个函数,每次调用返回下一个质数,要求不使用全局变量,且函数本身不接受任何参数 从题目的要求可以知道,这个函数每次调用都会返回一个质数,也就是说每次调用后都会返回一个函数。 首先我们定义一个判断一个数是否为质数的方法: function isPrime(num) { for (let i = ; i <= Math.sqrt(num); i++) { if (num % i === ) { return false } } return true } 传统的方式是使用闭包方法来解决: function primeHandler() { let prime = return () => { while (true) { prime++ if (isPrime(prime)) { return prime } } } } const getPrime = primeHandler() console.log(getPrime()); // 2 console.log(getPrime()); // 3 console.log(getPrime()); // 5 既然是单步执行的,那么我们就可以使用迭代器方式实现: var prime = {} prime[Symbol.iterator] = function() { let prime = ; return { next() { while(true) { prime++ if (isPrime(prime)) { return prime; } } } } } var getPrime = prime[Symbol.iterator]().next; console.log(getPrime()); // 2 console.log(getPrime()); // 3 上一个实例我们知道实现迭代器的方式是很麻烦的,可以使用生成器函数去替代迭代器的功能,所以上面的代码可以使用生成器函数改造如下: function* primeGenerator () { let prime = while (true) { prime++ if (isPrime(prime)) { yield prime } } } var getPrime = primeGenerator().next().value console.log(getPrime()); // 2 console.log(getPrime()); // 3 本节我们主要学习了生成器的概念和用法,需要生成器对象是由生成器函数返回的结果,生成器对象是遵守迭代协议和迭代器协议实现的 Iterable 接口。生成器其实就是对迭代器的应用。另外,通过两个案例更加深刻地理解了生成器的应用场景,对比了生成器和迭代器的不同。 (编辑:汽车网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |