新浦京81707con > 首页 > 和生成器,迭代器与生成器

原标题:和生成器,迭代器与生成器

浏览次数:194 时间:2019-05-06

迭代器

迭代器是被设计专用于迭代的靶子,带有一定接口。全体的迭代器对象都有next方法,会回来三个结实对象。该结果对象有五个性子:对应下一个值的value,以及贰个布尔类型的done,其值为true时表示未有更多对值可供使用。

//es5创建迭代器
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());//{done:false,value:1}
console.log(iterator.next());//{done:false,value:2}
console.log(iterator.next());//{done:false,value:3}
console.log(iterator.next());//{done:true,value:"undefined"}
console.log(iterator.next());//{done:true,value:"undefined"}

详谈ES陆中的迭代器(Iterator)和生成器(Generator),es陆iterator

日前的话

用循环语句迭代数据时,须求求开首化3个变量来记录每3回迭代在数额集结中的地点,而在不少编制程序语言中,已经开头通进度序化的章程用迭代器对象回来迭代进程中集中的每三个成分

迭代器的施用能够比不小地简化数据操作,于是ES陆也向JS中增添了那一个迭代器性情。新的数组方法和新的汇集类型(如Set集结与Map集结)都凭仗迭代器的落到实处,那几个新特征对于神速的数码管理来讲是必需的,在言语的其它特色中也都有迭代器的人影:新的for-of循环、展开运算符(...),以致连异步编制程序都能够选择迭代器

正文将详细介绍ES6中的迭代器(Iterator)和生成器(Generator)

引入

下边是1段正式的for循环代码,通过变量i来追踪colors数组的目录,循环每便实行时,如若i小于数COO度len则加一,并施行下三遍循环

var colors = ["red", "green", "blue"];
for (var i = 0, len = colors.length; i < len; i  ) {
 console.log(colors[i]);
}

固然循环语句语法轻松,但倘若将多少个巡回嵌套则须要追踪八个变量,代码复杂度会大大扩大,一相当大心就荒唐选取了别样for循环的追踪变量,从而致使程序出错。迭代器的产出目的在于祛除那种复杂并压缩循环中的错误

迭代器

迭代器是1种特有目的,它兼具局地特地为迭代进程规划的专有接口,全体的迭代器对象都有2个next()方法,每一次调用都回去3个结实对象。结果对象有两脾个性:1个是value,表示下多少个将在再次来到的值;另一个是done,它是2个布尔类型的值,当未有越多可回到数据时回来true。迭代器还会保留二个里边指针,用来针对当前集结中值的职责,每调用三回next()方法,都会重返下3个可用的值

一旦在终极四个值再次来到后再调用next()方法,那么重返的对象中品质done的值为true,属性value则含有迭代器最后回到的值,那么些重回值不是数据集的1部分,它与函数的重回值类似,是函数调用进度中最后一遍给调用者传递新闻的办法,若是未有相关数据则再次回到undefined

上边用ES5的语法成立2个迭代器

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

在上头那段代码中,createIterator()方法返回的靶子有2个next()方法,每便调用时,items数组的下八个值会作为value再次回到。当i为三时,done变为true;此时三朝表明式会将value的值设置为undefined。最后一回调用的结果与ES6迭代器的末梢回到机制就如,当数码集被用尽后会重返最后的剧情

地点那么些示例很复杂,而在ES陆中,迭代器的编辑规则也一如在此以前复杂,但ES六并且还引进了一个生成器对象,它可以让创设迭代器对象的进程变得更简便

生成器

生成器是壹种回到迭代器的函数,通过function关键字后的星号(*)来表示,函数中会用到新的基本点字yield。星号能够紧挨着function关键字,也能够在中等增多贰个空格

// 生成器
function *createIterator() {
 yield 1;
 yield 2;
 yield 3;
}
// 生成器能像正规函数那样被调用,但会返回一个迭代器
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3

在这么些示例中,createlterator()前的星号申明它是3个生成器;yield关键字也是ES6的新特色,能够通过它来内定调用迭代器的next()方法时的重返值及再次来到顺序。生成迭代器后,三番五次二次调用它的next()方法重回三个不等的值,分别是1、二和三。生成器的调用进程与别的函数同样,最后回到的是开创好的迭代器

生成器函数最有趣的一些是,每当推行完一条yield语句后函数就会自行甘休实践。比如,在上边这段代码中,实施完语句yield 壹之后,函数便不再推行其余任何语句,直到再也调用迭代器的next()方法才会继续施行yield 二语句。生成器函数的那种中止函数执行的力量有为数不少有意思的运用

利用yield关键字能够回去任何值或表明式,所以能够因而生成器函数批量地给迭代器添美元素。比方,可以在循环中运用yield关键字

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循环不断从数组中生成新的因素放入迭代器中,每境遇2个yield语句循环都会结束;每一次调用迭代器的next()方法,循环会继续运维并施行下一条yield语句

生成器函数是ES六中的3个重大特征,能够将其用于全部协理函数使用的地点

【使用范围】

yield关键字只可在生成器内部采取,在此外地点使用会导致程序抛出荒唐

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

从字面上看,yield关键字确实在createlterator()函数内部,不过它与return关键字同样,2者都无法穿透函数边界。嵌套函数中的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循环

[注意]不能够用箭头函数来成立生成器

【生成器对象的章程】

由于生成器自个儿便是函数,因此能够将它们增加到目的中。比方,在ES伍作风的靶子字面量中,能够透过函数表明式来创建生成器

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

也能够用ES陆的函数方法的简写格局来成立生成器,只需在函数名前增添一个星号(*)

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通过点名的函数能够再次回到2个作用于附属对象的迭代器。在ES陆中,全数的集中对象(数组、Set集结及Map集合)和字符串都以可迭代对象,这一个目标中都有暗中认可的迭代器。ES6中新加盟的性状for-of循环须要用到可迭代对象的那几个职能

[注意]由于生成器暗中认可会为Symbol.iterator属性赋值,由此具备通过生成器成立的迭代器都以可迭代对象

壹伊始,大家曾涉及过巡回之中索引追踪的相干难点,要消除那一个难题,要求八个工具:1个是迭代器,另多少个是for-of循环。如此一来,便无需再追踪整个集合的目录,只需关注会集中要拍卖的原委

for-of循环每试行1遍都会调用可迭代对象的next()方法,并将迭代器重返的结果对象的value属性存款和储蓄在3个变量中,循环将持续进行那1历程直到回到对象的done属性的值为true。这里有个示范

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

那段for-of循环的代码通过调用values数组的Symbol.iterator方法来获得迭代器,那1历程是在JS引擎背后完成的。随后迭代器的next()方法被频仍调用,从其重返对象的value属性读取值并蕴藏在变量num中,依次为一、二和三,当结果对象的done属性值为true时轮回退出,所以num不会被赋值为undefined

即便只需迭代数组或集合中的值,用for-of循环代替for循环是个不错的精选。比较传统的for循环,for-of循环的主宰原则更简短,无需追踪复杂的规则,所以更加少出错

[注意]要是将for-of语句用于不可迭代对象、null或undefined将会形成程序抛出荒唐

【访问默许迭代器】

可以由此Symbol.iterator来访问对象暗许的迭代器

let values = [1, 2, 3];
let iterator = values[Symbol.iterator]();
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 }"

在那段代码中,通过Symbol.iterator获取了数组values的暗中认可迭代器,并用它遍历数组中的成分。在JS引擎中执行for-of循环语句时也会有接近的管理进程

由于具备Symbol.iterator属性的对象都有暗中同意的迭代器,由此得以用它来检查测试对象是还是不是为可迭代对象

function isIterable(object) {
 return typeof object[Symbol.iterator] === "function";
}
console.log(isIterable([1, 2, 3])); // true
console.log(isIterable("Hello")); // true
console.log(isIterable(new Map())); // true
console.log(isIterable(new Set())); // true
console.log(isIterable(new WeakMap())); // false
console.log(isIterable(new WeakSet())); // false

这里的islterable()函数能够检查钦点对象中是还是不是存在暗中认可的函数类型迭代器,而for-of循环在试行前也会做一般的检讨

除了利用内建的可迭代对象类型的Symbol.iterator,也足以使用Symbol.iterator来成立属于本人的迭代器

【成立可迭代对象】

默许情形下,开辟者定义的靶子都是不足迭代对象,但假如给Symbol.iterator属性加多3个生成器,则足以将其改为可迭代对象

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) {
 //1
 //2
 //3
 console.log(x);
}

在这么些示例中,先创建三个生成器(注意,星号依旧在品质名前)并将其赋值给指标的Symbol.iterator属性来创立暗许的迭代器;而在生成器中,通过for-of循环迭代this.items并用yield再次来到每三个值。collection对象私下认可迭代器的归来值由迭代器this.items自动生成,而非手动遍历来定义重回值

【张开运算符和非数组可迭代对象】

透过开始展览运算符(...)能够把Set群集转变到四个数组

let set = new Set([1, 2, 3, 3, 3, 4, 5]),
array = [...set];
console.log(array); // [1,2,3,4,5]

那段代码中的张开运算符把Set会集的保有值填充到了1个数组字面量里,它能够操作所有可迭代对象,并基于默许迭代器来抉择要引用的值,从迭代器读取全体值。然后遵照再次回到顺序将它们依次插入到数组中。Set集结是2个可迭代对象,展开运算符也足以用来其余可迭代对象

let map = new Map([ ["name", "huochai"], ["age", 25]]),
array = [...map];
console.log(array); // [ ["name", "huochai"], ["age", 25]]

实行运算符把Map集合转换成包罗多个数组的数组,Map集结的暗许迭代器重回的是多组键值对,所以结果数组与试行new Map()时传出的数组看起来同样

在数组字面量中得以频仍运用实行运算符,将可迭代对象中的多少个因素依次插入新数组中,替换原先张开运算符所在的任务

let smallNumbers = [1, 2, 3],
bigNumbers = [100, 101, 102],
allNumbers = [0, ...smallNumbers, ...bigNumbers];
console.log(allNumbers.length); // 7
console.log(allNumbers); // [0, 1, 2, 3, 100, 101, 102]

创办二个变量allNumbers,用展开运算符将smallNumbers和bigNumbers里的值依次增多到allNumbers中。首先存入0,然后存入small中的值,最终存入bigNumbers中的值。当然,原始数组中的值只是被复制到allNumbers中,它们本身并未有改观

由于开始展览运算符能够功能于自由可迭代对象,由此假设想将可迭代对象调换为数组,那是最简便易行的方式。既能够将字符串中的每二个字符(不是编码单元)存入新数组中,也能够将浏览器中NodeList对象中的每二个节点存入新的数组中

内建迭代器

迭代器是ES陆的二个重视组成都部队分,在ES陆中,已经默感觉好些个内建项目提供了内建迭代器,唯有当那么些内建迭代器不能够完毕目的时才须求团结创立。日常来讲当定义本人的靶子和类时才会遇上那种情景,不然,完全能够注重内建的迭代器完结专业,而最常使用的大概是会合的那么些迭代器

【会集对象迭代器】

在ES陆中有三种类型的集纳对象:数组、Map集结与Set会集

为了越来越好地访问对象中的内容,那三种对象都内建了以下两种迭代器

entries() 再次来到2个迭代器,其值为多少个键值对
values() 重返2个迭代器,其值为集聚的值
keys() 重返一个迭代器,其值为汇集中的全数键名

调用以上二个措施都能够访问集结的迭代器

entries()迭代器

每便调用next()方法时,entries()迭代器都会回到贰个数组,数组中的七个成分分别表示集合中种种成分的键与值。若是被遍历的对象是数组,则率先个要素是数字类型的目录;要是是Set集结,则第一个成分与第四个因素都以值(Set集结中的值被同时作为键与值使用);假使是Map群集,则率先个成分为键名

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ES6");
data.set("format", "ebook");
for (let entry of colors.entries()) {
 console.log(entry);
}
for (let entry of tracking.entries()) {
 console.log(entry);
}
for (let entry of data.entries()) {
 console.log(entry);
}

调用console.log()方法后输出以下内容

[0, "red"]
[1, "green"]
[2, "blue"]
[1234, 1234]
[5678, 5678]
[9012, 9012]
["title", "Understanding ES6"]
["format", "ebook"]

在那段代码中,调用各样群集的entries()方法得到一个迭代器,并利用for-of循环来遍历成分,且经过console将每三个目的的键值对出口出来

values()迭代器

调用values()迭代器时会再次来到集结中所存的全部值

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ES6");
data.set("format", "ebook");
for (let value of colors.values()) {
 console.log(value);
}
for (let value of tracking.values()) {
 console.log(value);
}
for (let value of data.values()) {
 console.log(value);
}

调用console.log()方法后输出以下内容

"red"
"green"
"blue"
1234
5678
9012
"Understanding ES6"
"ebook"

如上所示,调用values()迭代器后,重临的是各样群集中隐含的的确数据,而不带有数据在集合中的地点音信

keys()迭代器

keys()迭代器会回到集合中留存的每三个键。假若遍历的是数组,则会回来数字类型的键,数组本身的其余属性不会被重临;倘使是Set会集,由于键与值是平等的,因而keys()和values()重临的也是毫无2致的迭代器;假若是Map集结,则keys()迭代器会回来每一种独立的键

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ES6");
data.set("format", "ebook");
for (let key of colors.keys()) {
 console.log(key);
}
for (let key of tracking.keys()) {
 console.log(key);
}
for (let key of data.keys()) {
 console.log(key);
}

调用console.log()方法后输出以下内容

0
1
2
1234
5678
9012
"title"
"format"

keys()迭代器会得到colors、tracking和data那1个集聚中的每三个键,而且分别在一个for-of循环之上将那几个键名打字与印刷出来。对于数组对象的话,无论是或不是为数组增添命名属性,打印出来的都是数字类型的目录;而for-in循环迭代的是数组属性而不是数字类型的目录

不等集合类型的私下认可迭代器

每一种集结类型都有二个默许的迭代器,在for-of循环中,假设未有显式钦定则使用暗中认可的迭代器。数组和Set集结的暗中认可迭代器是values()方法,Map集合的暗许迭代器是entries()方法。有了那个私下认可的迭代器,能够更轻易地在for-of循环中利用集合对象

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ES6");
data.set("format", "print");
// 与使用 colors.values() 相同
for (let value of colors) {
 console.log(value);
}
// 与使用 tracking.values() 相同
for (let num of tracking) {
 console.log(num);
}
// 与使用 data.entries() 相同
for (let entry of data) {
 console.log(entry);
}

上述代码未钦点迭代器,所以将应用暗中同意的迭代器。数组、Set集结及Map集结的私下认可迭代器也会反应出这几个目的的开头化进程,所以那段代码会输出以下内容

"red"
"green"
"blue"
1234
5678
9012
["title", "Understanding ES6"]
["format", "print"]

默许意况下,假使是数组和Set集结,会挨个再次来到集结中装有的值。假设是Map集结,则依照Map构造函数参数的格式再次回到同样的数组内容。而WeakSet集结与WeakMap集合就从不内建的迭代器,由于要管理弱引用,由此不只怕适用地通晓集结中留存的值,也就没办法迭代那些聚焦了

【字符串迭代器】

自ES伍透露之后,JS字符串慢慢变得更像数组了,举例,ES5正式分明能够通过方括号访问字符串中的字符(也便是说,text[0]能够获得字符串text的率先个字符,并由此及彼)。由于方括号操作的是编码单元而非字符,由此不恐怕精确访问双字节字符

var message = "A  
		

本文由新浦京81707con发布于首页,转载请注明出处:和生成器,迭代器与生成器

关键词: 新浦京81707con

上一篇:字符串的常用操作

下一篇:没有了

Copyright © 2018 http://www.regboy.com All Rights Reserved |

新浦京81707con(www.regboy.com)提供安全免费的在线小游戏,www.81707com拥有较好的网速之外,新浦京81707con致力于对大量的用户需求进行分析,点击登录。