博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
迭代器与生成器
阅读量:4600 次
发布时间:2019-06-09

本文共 5342 字,大约阅读时间需要 17 分钟。

一、迭代器的特征

迭代器有一个next()方法,每次调用时会返回一个对象,该对象的结构为{value:xxx,done:true},其中value表示下次应该返回的值,done表示是否还有值可提供。

当没有值可提供时,done为true,如果迭代器在迭代结束时使用了return xxx,则value为xxx,否则为undefined。

function createIterator(items) {     var i = 0;     return {          next: function() {              var done = (i >= items.length);              var value = !done ? items[i++] : undefined;              return { done: done, value: value };         }     }; } var 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 }"

二、迭代器的作用

  • 用于依序访问可迭代的对象,可迭代的对象为:Array、Map、Set、String、TypedArray、函数的 arguments对象、NodeList 对象
  • 可迭代对象( iterable )内部有一个 Symbol.iterator 属性。这 个 Symbol.iterator 知名符号定义了为指定对象返回迭代器的函数。
  • 集合类型有三种迭代器:

             entries() :返回一个包含键值对的迭代器,此迭代器是Map类型默认的迭代器;Array把下标做为key;而Set则把值作为key

             values() :返回一个包含集合中的值的迭代器,此迭代器是Set与Array的默认迭代器

             keys() :返回一个包含集合中的键的迭代器。

let colors = [ "red", "green", "blue" ];for (let entry of colors.entries()) {     console.log(entry); }
  • 当使用for of访问对象时,如果没有显示指定使用哪个迭代器,则调用每种类型默认的。
  • 在for of中可以使用break语句跳出for of,可使用throw new Error('xxx')终止程序继续运行

三、使用生成器创造迭代器

 在function后面加一个*号,然后在函数内部使用yield标识符指定调用next时应该返回的值

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 }"

 

生成器一是特殊的函数,因此可以作为对象的方法

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

 

yield关键字不能放在生成器内部的函数中,下面代码是错误的

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

四、把不可迭代的对象变成可迭代的

默认情况下,我们自己创建的对象是不可迭代的,可以自己定义一个生成器让对象变成可迭代对象

let collection = {     items: [],     *[Symbol.iterator]() {         for (let item of this.items) {             yield item;         }     } }; collection.items.push(1); collection.items.push(2);collection.items.push(3); for (let x of collection) {     console.log(x); }

 五、迭代器的next和生成器的yield进行通讯

1. 在生成器中,当下一个yield的值依赖上一个yield的结果进行计算时,可以使用迭代器的next(xxx)方法传值来覆盖上一个yield的结果,例子

function *createIterator() { let first = yield 1; let second = yield first + 2; // 4 + 2 yield 3; }let iterator = createIterator();console.log(iterator.next());  // 第1个next传值不会生效,因为没有上一个yieldconsole.log(iterator.next(4)); // first变成了4,结果为"{ value: 6, done: false }"console.log(iterator.next(5)); // 第3条yield不依赖上一条yield的结果进行计算,传值无效console.log(iterator.next()); // "{ value: undefined, done: true }"

 2.通过iterator.throw()向生成器传递错误来阻止后续的迭代

function *createIterator() { let first = yield 1; let second = yield first + 2; // 4 + 2 yield 3; }let iterator = createIterator();console.log(iterator.next());  // 第1个next传值不会生效,因为没有上一个yieldconsole.log(iterator.throw('后面的别运行了'));console.log(iterator.next(5)); // 不会执行console.log(iterator.next()); //  不会执行

 3.在生成器中通过try catch屏蔽错误

下面的代码在第二个next前抛出了错误,也就是生成器在执行第一条yield时会出现错误,采用try catch处理后,生成器可以继续向下运行

function *createIterator() { let first; try{    first = yield 1; } catch(ex){     first=100 } let second = yield first + 2; // 4 + 2 yield 3; }let iterator = createIterator();console.log(iterator.next());console.log(iterator.throw('后面的别运行了')); //{value: 102, done: false}console.log(iterator.next(5)); //没有依赖上一个yield的结果进行计算,返回 {value: 3, done: false}console.log(iterator.next()); // {value: undefined, done: true}

 六、生成器合并

1. 可以将多个生成器合并成一个,方法是新写一个生成器函数,然后把生成器内部的各个yield分别指向不同的生成器。

合并后程序是按顺序执行的,即先把第一个生成器内的所有yield执行完再执行下一个。

function *createOne() {   for(let i=4;i<7;i++){       yield i   }}function *createTwo() {   for(let i=7;i<10;i++){       yield i   }}function *createAll(){    yield *createOne();    yield *createTwo();}var iterator=createAll();for(let i of iterator){    console.log(i)}

2.后一个生成器依赖前一个生成器的结果

可以在前一个迭代器中使用return返回一个迭代结束后的值,然后把该值传给第二个迭代器的yield使用

function *createOne() {   for(let i=4;i<7;i++){       yield i   }   return 101;}function *createTwo(oneResult) {   for(let i=7;i<10;i++){       yield i+oneResult   }}function *createAll(){    let result=yield *createOne();    yield *createTwo(result);}var iterator=createAll();for(let i of iterator){    console.log(i)}

 七、任务执行器

每次调用next()时都会执行yield上面的语句,还有一条yield后面的语句。

function* gen(){    console.log('one')    yield 1;    console.log('b')    yield 2;    yield 3;}var t=gen();console.log(t.next());console.log(t.next());

 那么我们能不能做一个任务运行器自动执行next呢?答案是使用递归

let run=function*(generator){    let task=generator();    let result=task.next();启动任务运行器    function autoNext(){        if(!task.done){            result=task.next(result.value)            step();//递归执行下一个next()        }    }    autoNext();}run(function*() {    let value = yield 1;    console.log(value); // 1    value = yield value + 3;    console.log(value); // 4});

 

转载于:https://www.cnblogs.com/94pm/p/9653152.html

你可能感兴趣的文章
dva reduxRouter 跳转路由的参数
查看>>
Code Pages
查看>>
How do I force my .NET application to run as administrator?
查看>>
应该知道的30个jQuery代码开发技巧
查看>>
PHP与ASP.NET的优劣比较
查看>>
Dapper使用动态参数
查看>>
Vue的安装及使用快速入门
查看>>
Java_包装类
查看>>
ubuntu14.04安装与配置nginx服务器
查看>>
利用/dev/urandom文件创建随机数
查看>>
js遍历获取表格内数据方法
查看>>
DVWA渗透测试环境搭建
查看>>
bzoj2219: 数论之神
查看>>
bzoj 3261: 最大异或和
查看>>
「折腾」用word发布博客
查看>>
C# 基本函数
查看>>
iOS审核秘籍】提审资源检查大法
查看>>
辗转相除法
查看>>
A2-02-29.DML-MySQL DELETE
查看>>
vue 路由跳转
查看>>