首页 > 编程语言 >JavaScript中的Generator生成器的基本用法

JavaScript中的Generator生成器的基本用法

时间:2022-10-22 01:33:27浏览次数:83  
标签:console 函数 Generator JavaScript 生成器 yield next log

ES6入门-阮一峰:Generator 函数

1. 介绍

Generator 生成器是 ES6 提供的一种异步编程解决方案。是一个极为灵活的结构,拥有在函数块中暂停和恢复代码执行的能力。

执行 Generator 生成器 会返回一个 ( Iterator )迭代器对象,也就是说:Generator 函数除了状态机,还是一个迭代器对象生成函数,返回的迭代器对象,可以依次遍历 Generator 函数内部的每一个状态。

形式上,Generator 函数就是一个普通函数,但是又两个特征:

  1. function 关键字 和 函数名中间有一个 * 号
  2. 函数体内部使用 yield 表达式,定义不同的内部状态
function* generatorFn() {
    yield 'hello'
    yield 'world'
    return 'ending'
}

var hw = generatorFn()

/****************
	上面代码定义了一个 Generator 函数 ( generatorFn ),  
	它内部有两个 yield 表达式 ( 'hello' 和 'world' ), 
	即该函数有三个状态: hello、world 和 return 语句 ( 结束执行 )。
*****************/

2. next() 方法

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

下一步,必须调用 Iterator 遍历器对象 的 next 方法,使指针移向下一个状态。也就是说,每次调用 next 方法,内部指针就会从函数头部或者上一次停下的位置开始执行,直到遇到下一个 yield 表达式 ( 或 return 语句 ) 为止。

换而言之,就是说 yield 语句是暂停执行的标记,而 next 方法则为恢复执行。

function* generatorFn() {
    yield 'hello'
    yield 'world'
    return 'ending'
}

var hw = generatorFn()

hw.next()
hw.next()
hw.next()
hw.next()
// { value: 'hello', done: false } 
// { value: 'world', done: false }
// { value: 'ending', done: true }
// { value: undefined, done: true }
function* gen() {
    console.log(1)
    yield '嘿嘿'

    console.log(2)
    yield '呵呵'

    console.log(3)
    yield '哈哈'

    console.log(4)
}

let test = gen()
test.next()
test.next()
test.next()
test.next()
/*
1
2
3
4
*/

yield 表达式本身没有返回值,或者说总是返回 undefined。next() 方法可以携带一个参数,这个参数会被当成上一个 yield 表达式的返回值。

注意:因为每个 next() 方法传入的值都是上一个 yield 表达式的返回值,所以第一个 next() 传入的值是无效的,V8 引擎直接忽略第一次使用 next() 时的参数,只有第二次使用才会生效。

function* gen(arg) {
    console.log(arg);

    let one = yield 111;
    console.log(one);

    let two = yield 222
    console.log(two);

    let three = yield 333
    console.log(three);
}

// 执行获取迭代器对象
let test = gen('AAA')

test.next('EE'); // 第一个 next 的参数会被忽略

// next 方法可以传入实参
test.next('BBB');
test.next('CCC');
test.next('DDD');

// AAA
// BBB
// CCC
// DDD

3. 使用 for...of 遍历

使用 for...of... 可以直接循环 Generator 函数运行时生成的 Iterator 对象,且此时不再需要调用 next 方法。

function* foo() {
  yield 1
  yield 2
  yield 3
  yield 4
  yield 5
  return 6
}

for (let v of foo()) {
  console.log(v)
}

// 1 2 3 4 5
// 之所以没有输出 6,是因为一旦 next 返回对象的 done 属性为 true, for...of 循环就会自动终止,且不包含该返回对象。

3.1 实现斐波那契数列

第一次执行,先定义了 prev (上一个), curr (现在的), 并且赋值为 0、1,然后进入死循环返回 curr。

返回后输出,回到函数,将 curr 和 prev 的值相加后赋值给 curr,并且把 curr 的值交给 prev ,这两个需要使用解构赋值一起执行,否则需要一个中转值先承载某一个的值。

function* fibonacci() {
  let [prev, curr] = [0, 1]
  for (;;) {
    yield curr
      ;[prev, curr] = [curr, prev + curr]
  }
}

for (let n of fibonacci()) {
  if (n > 1000) break
  console.log(n)
}

// 不使用解构赋值语法

function* fibonacci() {
  let prev = 0
  let curr = 1
  let temp = null

  for (;;) {
    yield curr;
    temp = prev
    prev = curr
    curr = temp + curr
  }
}

for (let n of fibonacci()) {
  if (n > 1000) break
  console.log(n)
}

4. 错误捕获 throw()

Generator 函数返回的遍历器对象,都有一个 throw 方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。

如果没有在 Generator 函数体内设置 try... catch 代码块,那么抛出的错误会直接被外部的 catch 代码块捕获,如果 Generator 函数体内部和外部都没有部署 try...catch 代码块,那么程序将报错,直接中断执行。

throw 抛出的错误如果想被内部捕获,最少需要执行一次 next 方法。如果没有执行就会直接被外部的 catch 代码块捕获。

var g = function* () {
  try {
    yield
  } catch (e) {
    console.log('内部捕获', e)
  }
}

var i = g()
i.next()

try {
  i.throw('a')
  i.throw('b')
} catch (e) {
  console.log('外部捕获', e)
}

// 内部捕获 a
// 外部捕获 b

上面代码中,遍历器对象连续抛出两个错误。其中,第一个错误被 Generator 函数内部的 catch 语句捕获。第二个错误由于 Generator 函数内部的 catch 语句已经执行了,不会再执行,所以被外部的 catch 语句捕获了。

5. 停止 Generator 函数

Generator 函数返回的遍历器对象,还有一个 return() 方法,可以返回给指定的值,并且可以终结遍历 Generator 函数。

调用 return() 方法后,返回值的 value 属性就是 return() 方法接收的参数,如果不带参数,那么默认返回的 value 属性就是 undefined。

function* gen() {
  yield 1
  yield 2
  yield 3
}

var g = gen()

console.log(g.next())
console.log(g.return('foo'))
// console.log(g.return())
console.log(g.next())

// {value: 1, done: false}
// {value: 'foo', done: true}
// {value: undefined, done: true}  // 这是注释的那行的输出
// {value: undefined, done: true}

标签:console,函数,Generator,JavaScript,生成器,yield,next,log
From: https://www.cnblogs.com/bkzj/p/16815177.html

相关文章

  • JavaScript实现数据结构 -- 集合
    集合集合是一种无序且唯一的数据结构,在ES6中有集合Set。集合的常用操作去重使用Set结合展开运算符实现数组去重。判断元素是非在集合中使用Set的has方法判断元素是......
  • JavaScript实现数据结构 -- 字典
    字典字典与集合类似,也是一种存储唯一值的数据结构,字典以键值对的形式进行存储,在ES6中有字典Map。字典的常用操作增使用set()方法可以向字典中添加新成员,可连续添加。......
  • JavaScript禁止在网页中右键和选取功能
    JavaScript禁止在网页中右键和选取功能document.oncontextmenu=newFunction('event.returnValue=false;')document.onselectstart=newFunction('event.returnValu......
  • JavaScript中的bind使用技巧
    functionf(){returnthis.a;}//bind绑定会创一个与f具有相同函数体和作用域的新函数,在这个新函数中,this将会永久的绑定第一个参数。......
  • NodeJS & Dapr Javascript SDK 官方使用指南
    Dapr是一个可移植的、事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的、无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言和开发框......
  • Cannot resolve symbol 'UniqueNameGenerator'
    Cannotresolvesymbol'UniqueNameGenerator'昨天还能跑的项目,今天好多飘红首先看JDK和Maven是否还是都配好了的(这块我的没问题)然后清理了下缓存,解决......
  • 在b/s开发中经常用到的javaScript技术整理,js,javascript参考,js参考
    一、验证类1、数字验证内1.1整数1.2大于0的整数(用于传来的ID的验证)1.3负整数的验证1.4整数不能大于iMax1.5整数不能小于iMin2、时间类2.1短时间,形如(1......
  • javascript 的setTimeOut 中this指向及外部参数传参
    //外部的参数传参数,放到第三项及以后就可以myArray=['zero','one','two'];myArray.myMethod=function(sProperty){consol......
  • JavaScript获取两个数组数组的差集
    JavaScript获取两个数组数组的差集JavaScript在ES6中增加了很多Array对象的方法,这让我们在做数组元素操作的时候方便很多。以下便是分别通过Array的some,find,findIndex......
  • JavaScript 设计模式之代理模式
    代理模式,代理(proxy)是一个对象,它可以用来控制对另一个对象的访问。现在页面上有一个香港回归最想听的金典曲目列表:<ulid="container"><li>我的中国心</li><li>东方......