详谈ES6中的迭代器(Iterator)和生成器(Generator)(2)

function *createIterator(items) { for (let i = 0; i < items.length; i++) { yield items[i]; } } let iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }" // 之后的所有调用 console.log(iterator.next()); // "{ value: undefined, done: true }"

在此示例中,给生成器函数createlterator()传入一个items数组,而在函数内部,for循环不断从数组中生成新的元素放入迭代器中,每遇到一个yield语句循环都会停止;每次调用迭代器的next()方法,循环会继续运行并执行下一条yield语句

生成器函数是ES6中的一个重要特性,可以将其用于所有支持函数使用的地方

【使用限制】

yield关键字只可在生成器内部使用,在其他地方使用会导致程序抛出错误

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

从字面上看,yield关键字确实在createlterator()函数内部,但是它与return关键字一样,二者都不能穿透函数边界。嵌套函数中的return语句不能用作外部函数的返回语句,而此处嵌套函数中的yield语句会导致程序抛出语法错误

【生成器函数表达式】

也可以通过函数表达式来创建生成器,只需在function关键字和小括号中间添加一个星号(*)即可

let createIterator = function *(items) { for (let i = 0; i < items.length; i++) { yield items[i]; } }; let iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }" // 之后的所有调用 console.log(iterator.next()); // "{ value: undefined, done: true }"

在这段代码中,createlterator()是一个生成器函数表达式,而不是一个函数声明。由于函数表达式是匿名的,因此星号直接放在function关键字和小括号之间。此外,这个示例基本与前例相同,使用的也是for循环

[注意]不能用箭头函数来创建生成器

【生成器对象的方法】

由于生成器本身就是函数,因而可以将它们添加到对象中。例如,在ES5风格的对象字面量中,可以通过函数表达式来创建生成器

var o = { createIterator: function *(items) { for (let i = 0; i < items.length; i++) { yield items[i]; } } }; let iterator = o.createIterator([1, 2, 3]);

也可以用ES6的函数方法的简写方式来创建生成器,只需在函数名前添加一个星号(*)

var o = { *createIterator(items) { for (let i = 0; i < items.length; i++) { yield items[i]; } } }; let iterator = o.createIterator([1, 2, 3]);

这些示例使用了不同于之前的语法,但它们的功能实际上是等价的。在简写版本中,由于不使用function关键字来定义createlterator()方法,因此尽管可以在星号和方法名之间留白,但还是将星号紧贴在方法名之前

【状态机】

生成器的一个常用功能是生成状态机

let state = function*(){ while(1){ yield 'A'; yield 'B'; yield 'C'; } } let status = state(); console.log(status.next().value);//'A' console.log(status.next().value);//'B' console.log(status.next().value);//'C' console.log(status.next().value);//'A' console.log(status.next().value);//'B'

可迭代对象

可迭代对象具有Symbol.iterator属性,是一种与迭代器密切相关的对象。Symbol.iterator通过指定的函数可以返回一个作用于附属对象的迭代器。在ES6中,所有的集合对象(数组、Set集合及Map集合)和字符串都是可迭代对象,这些对象中都有默认的迭代器。ES6中新加入的特性for-of循环需要用到可迭代对象的这些功能

[注意]由于生成器默认会为Symbol.iterator属性赋值,因此所有通过生成器创建的迭代器都是可迭代对象

一开始,我们曾提到过循环内部索引跟踪的相关问题,要解决这个问题,需要两个工具:一个是迭代器,另一个是for-of循环。如此一来,便不需要再跟踪整个集合的索引,只需关注集合中要处理的内容

for-of循环每执行一次都会调用可迭代对象的next()方法,并将迭代器返回的结果对象的value属性存储在一个变量中,循环将持续执行这一过程直到返回对象的done属性的值为true。这里有个示例

let values = [1, 2, 3]; for (let num of values) { //1 //2 //3 console.log(num); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wyjydf.html