当前位置:K88软件开发文章中心编程语言JavaScriptECMAScript → 文章内容

ECMAScript 6 Generator 函数

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-15 15:38:37

回对象的done属性为true,for...of循环就会中止,且不包含该返回对象,所以上面代码的return语句返回的6,不包括在for...of循环之中。下面是一个利用Generator函数和for...of循环,实现斐波那契数列的例子。function* fibonacci() { let [prev, curr] = [0, 1]; for (;;) { [prev, curr] = [curr, prev + curr]; yield curr; }}for (let n of fibonacci()) { if (n > 1000) break; console.log(n);}从上面代码可见,使用for...of语句时不需要使用next方法。利用for...of循环,可以写出遍历任意对象(object)的方法。原生的JavaScript对象没有遍历接口,无法使用for...of循环,通过Generator函数为它加上这个接口,就可以用了。function* objectEntries(obj) { let propKeys = Reflect.ownKeys(obj); for (let propKey of propKeys) { yield [propKey, obj[propKey]]; }}let jane = { first: 'Jane', last: 'Doe' };for (let [key, value] of objectEntries(jane)) { console.log(`${key}: ${value}`);}// first: Jane// last: Doe上面代码中,对象jane原生不具备Iterator接口,无法用for...of遍历。这时,我们通过Generator函数objectEntries为它加上遍历器接口,就可以用for...of遍历了。加上遍历器接口的另一种写法是,将Generator函数加到对象的Symbol.iterator属性上面。function* objectEntries() { let propKeys = Object.keys(this); for (let propKey of propKeys) { yield [propKey, this[propKey]]; }}let jane = { first: 'Jane', last: 'Doe' };jane[Symbol.iterator] = objectEntries;for (let [key, value] of jane) { console.log(`${key}: ${value}`);}// first: Jane// last: Doe除了for...of循环以外,扩展运算符(...)、解构赋值和Array.from方法内部调用的,都是遍历器接口。这意味着,它们都可以将Generator函数返回的Iterator对象,作为参数。function* numbers () { yield 1 yield 2 return 3 yield 4}// 扩展运算符[...numbers()] // [1, 2]// Array.form 方法Array.from(numbers()) // [1, 2]// 解构赋值let [x, y] = numbers();x // 1y // 2// for...of 循环for (let n of numbers()) { console.log(n)}// 1// 2Generator.prototype.throw()Generator函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在Generator函数体内捕获。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上面代码中,遍历器对象i连续抛出两个错误。第一个错误被Generator函数体内的catch语句捕获。i第二次抛出错误,由于Generator函数内部的catch语句已经执行过了,不会再捕捉到这个错误了,所以这个错误就被抛出了Generator函数体,被函数体外的catch语句捕获。throw方法可以接受一个参数,该参数会被catch语句接收,建议抛出Error对象的实例。var g = function* () { try { yield; } catch (e) { console.log(e); }};var i = g();i.next();i.throw(new Error('出错了!'));// Error: 出错了!(…)注意,不要混淆遍历器对象的throw方法和全局的throw命令。上面代码的错误,是用遍历器对象的throw方法抛出的,而不是用throw命令抛出的。后者只能被函数体外的catch语句捕获。var g = function* () { while (true) { try { yield; } catch (e) { if (e != 'a') throw e; console.log('内部捕获', e); } }};var i = g();i.next();try { throw new Error('a'); throw new Error('b');} catch (e) { console.log('外部捕获', e);}// 外部捕获 [Error: a]上面代码之所以只捕获了a,是因为函数体外的catch语句块,捕获了抛出的a错误以后,就不会再继续try代码块里面剩余的语句了。如果Generator函数内部没有部署try...catch代码块,那么throw方法抛出的错误,将被外部try...catch代码块捕获。var g = function* () { while (true) { yield; console.log('内部捕获', e); }};var i = g();i.next();try { i.throw('a'); i.throw('b');} catch (e) { console.log('外部捕获', e);}// 外部捕获 a上面代码中,Generator函数g内部没有部署try...catch代码块,所以抛出的错误直接被外部catch代码块捕获。如果Generator函数内部和外部,都没有部署try...catch代码块,那么程序将报错,直接中断执行。var gen = function* gen(){ yield console.log('hello'); yield console.log('world');}var g = gen();g.next();g.throw();// hello// Uncaught undefined上面代码中,g.throw抛出错误以后,没有任何try...catch代码块可以捕获这个错误,导致程序报错,中断执行。throw方法被捕获以后,会附带执行下一条yield语句。也就是说,会附带执行一次next方法。var gen = function* gen(){ try { yield console.log('a'); } catch (e) { // ... } yield console.log('b'); yield console.log('c');}var g = gen();g.next() // ag.throw() // bg.next() // c上面代码中,g.throw方法被捕获以后,自动执行了一次next方法,所以会打印b。另外,也可以看到,只要Generator函数内部部署了try...catch代码块,那么遍历器的throw方法抛出的错误,不影响下一次遍历。另外,throw命令与g.throw方法是无关的,两者互不影响。var gen = function* gen(){ yield console.log('hello'); yield console.log('world');}var g = gen();g.next();try { throw new Error();} catch (e) { g.next();}// hello// world上面代码中,throw命令抛出的错误不会影响到遍历器的状态,所以两次执行next方法,都进行了正确的操作。这种函数体内捕获错误的机制,大大方便了对错误的处理。多个yield语句,可以只用一个try...catch代码块来捕获错误。如果使用回调函数的写法,想要捕获多个错误,就不得不为每个函数内部写一个错误处理语句,现在只在Generator函数内部写一次catch语句就可以了。Generator函数体外抛出的错误,可以在函数体内捕获;反过来,Generator函数体内抛出的

上一页  [1] [2] [3] [4] [5] [6] [7]  下一页


ECMAScript 6 Generator 函数