ES6中迭代器与生成器知识浅析

news/2024/12/23 0:24:16/

ES5及以下版本对JS几种集合,要存取数据一般需要用循环语句来遍历,就要初始化一个或多个变量来记录每一次循环在数据集合中的位置或数据值。这里容易出现超出边界问题,造成程序出错。另外,对于多次循环也需要跟踪理清各个变量关系及存续问题,造成问题复杂化和不安全性。

ES6开始引入迭代器,它可以极大的简化数据操作,不需要额外的临时变量,本身带有内部查找及循环机制。对于程序员来说,只需要取用即可,取用一次,集合内部迭代器自动更新,并将指针指向下一个数据,完全不用操心遍历循环设计及控制问题。

在ES6中,Map集合等众多集合,JS本身就具有内建迭代器功能,可以直接使用其内联迭代器。而对于其他不具有迭代功能的集合或用户自己生成的集合,是不具有迭代器功能的。因此,生成器就是让用户在建立集合时就加入迭代功能,生成相关迭代语句,以便后面使用它时可以运用迭代功能。

 一、什么是迭代器

迭代器是一种特殊对象,它具有一些专门为迭代过程设计的专有接口,所有的迭代器都有一个next()方法,每次调用都返回一个结果对象。结果对象有两个属性:一个是value,表示下一个将要返回的值(对应调用next次数序号数据值),如果是最后一个或更后的调用next()方法,则value返回undefined;另一个是done,它是一个布尔类型的值,当没有可返回数据时返回true,有返回数据时为false。迭代器还会保存一个内部指针,用来指向当前集合中值的位置,每调用一次next()方法,都会返回下一个可能的值。

下面,用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());//输出结果:{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},之后所有调用next()方法都会是这个结果

二、什么是生成器

生成器是一种返回迭代器的函数,通过function关键字后的星号“*”来表示,函数中会用到新的关键字yield。星号紧挨着function关键字,也可以在中间添加一个空格。

//生成器

function *createIterator(){//*表示该函数是一个迭代器生成器

       yield 10;//yield关键字定义迭代器next()方法调用的返回值和顺序

       yield 20;

       yield 30;

}

//生成器的调用方式与普通函数相同,只不过返回的是一个迭代器

let iterator=createIterator();

console.log(iterator.next().value);//输出结果:10

console.log(iterator.next().value);//输出结果:20

console.log(iterator.next().value);//输出结果:30

console.log(iterator.next().value);//输出结果:undefined

三、生成器的使用方法要点

1.生成器yield并不是直接生成可迭代的集合对象

它只是生成一个迭代器,就象给一个运行员打了一针兴奋器,可以让该运动员比没打前跑得更快,而一个“yield  10;”语句,则是表示对于10这个数据可以用next()方法来加速查找(不需要循环结果及变量辅助)。

而要生成一个可迭代的集合对象,则是需要在生成集合对象的过程中,每一个数据生成时都要调用生成器函数,用yield去定义数据,这样最终形成的集合对象才是迭代集合对象。就如上面的生成器示例中,如果20和30不用yield去定义,则不能再第二次和第三次next()方法中取到这个两值,因为没有给它们生成可以用next方法迭代的过程。

2.使用yield关键字可以返回任何值或表达式

所以可以通过生成器函数批量地给迭代器添加元素。如下示例:

function *createIterator(items){

              for(let i=0;i<items.length;i++){

                yield items[i];

        }

}

let iterator=createIterator(["张小玉","孙大壮","李强"]);

console.log(iterator.next());//输出结果:{value:张小玉,done:false}

console.log(iterator.next());//输出结果:{value:孙大壮,done:false}

console.log(iterator.next());//输出结果:{value:李强,done:false}

console.log(iterator.next());//输出结果:{value:undefined,done:true}

3.yield关键字只可在生成器内部使用,在其他地方使用会报错

即使用在生成器内部的函数里使用也不行。也就是说,生成器函数中除了yield开头的语句,只能出现块状体{}中,如if{}、for(){}等作为yield生成有关的逻辑体外,其他无关的定义变量、赋值等语句,如果不是以yield开头,都将报错。

下面为错误的使用yield示例:

function *createIterator(items){//错误的使用yield

              items.forEach(function(item){

                     //语法错误,因为参数为函数,不属于块状作用域

                     yield item+1;//报错,Unexpected identifier 'item'

              });

}

let iterator=createIterator([1,2,3]);

console.log(iterator.next());//输出结果:无输出

console.log(iterator.next());//输出结果:无输出

console.log(iterator.next());//输出结果:无输出

console.log(iterator.next());//输出结果:无输出

下面为正确的使用yield示例:

function *createIterator(items){//正确的使用yield

        for(let item of items){

               yield item+1;

        };

}

let iterator=createIterator([1,2,3]);

console.log(iterator.next().value);//输出结果:2

console.log(iterator.next().value);//输出结果:3

console.log(iterator.next().value);//输出结果:4

console.log(iterator.next().value);//输出结果:undefined

4.生成器对象的方法

let o={

       createIterator:function *(items){

              for(let i=0;i<items.length;i++){

                     yield items[i];

              }

       }

};

let iterator=o.createIterator([1,2,3]);

console.log(iterator.next().value);//输出结果:1

console.log(iterator.next().value);//输出结果:2

console.log(iterator.next().value);//输出结果:3

console.log(iterator.next().value);//输出结果:undefined

四、具有内联迭代器的集合

在ES6中,已经默认为许多类型(主要有数组、Map集合和Set集合)建立了内联迭代器,只有当这些内联迭代器无法实现你的目标时才需要自己创建,并用生成器加入迭代器。

(一)通过Symbol.iterator来访问内联集合的默认迭代器

1.访问默认迭代器

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

2.创建可迭代对象

默认情况下,开发者定义的对象都是不可迭代的对象,但如果给Symbol.iterator属性添加一个生成器,则其也具有了默认迭代器。

let collection={//创建可迭代对象

       items:[],

       *[Symbol.iterator](){

              for(let item of thi.items){

                     yield item;

              }

       }

};

collection.items.push(10);//给对象赋值

collection.items.push(20);

collection.items.push(30);

let iterator=collection.items[Symbol.iterator]();//建立默认迭代器

console.log(iterator.next());//输出结果:{value:10,done:false}

console.log(iterator.next());//输出结果:{value:20,done:false}

console.log(iterator.next());//输出结果:{value:30,done:false}

console.log(iterator.next());//输出结果:{value:undefined,done:true}

(二)访问内联迭代器

内联集合有三个内联迭代器:

第一个:entries(),返回一个迭代器,其值为多个键值对

第二个:values(),返回一个迭代器,其值为集合的值

第三个:keys(),返回一个迭代器,其值为集合中的所有键名

1. entries()迭代器

每次调用next()方法时,都会返回一个数组,数组中的两个元素分别表示键与值。如果是数组,第一个是索引,每二个是值;如果是Set集合,两个都是值;如果是Map集合,第一个为键名,第二个为值。

let colors=["red","green","blue"]//数组集合

for(let entry of colors.entries()){

       console.log(entry);//输出结果:{[0,"red"],[1,"green"],[2,"blue"]}

}

let tracking=new Set([10,20,30]);//Set集合

for(let netry of tracking.entries()){

       console.log(netry);//输出结果:{[10,10],[20,20],[30,30]}

}

let data=new Map();//Map集合

data.set("第一名","张小玉");

data.set("第二名","孙大壮");

data.set("第三名","李强");

for(let entry of data.entries()){

       console.log(entry);//输出结果:{["第一名","张小玉"],["第二名","孙大壮"],["第三名","李强"]}

}

2. values()迭代器

调用values()迭代器时会返回集合中所存的所有值,如:

let colors=["red","green","blue"]//数组集合

for(let entry of colors.values()){

       console.log(entry);//输出结果:{"red","green","blue"}

}

let tracking=new Set([10,20,30]);//Set集合

for(let netry of tracking.values()){

       console.log(netry);//输出结果:{10,20,30}

}

let data=new Map();//Map集合

data.set("第一名","张小玉");

data.set("第二名","孙大壮");

data.set("第三名","李强");

for(let entry of data.values()){

       console.log(entry);//输出结果:{"张小玉","孙大壮","李强"}

}

3. keys()迭代器

调用keys()迭代器会返回集合中存在的键。

let colors=["red","green","blue"]//数组集合

for(let entry of colors.keys()){

       console.log(entry);//输出结果:{0,1,2}

}

let tracking=new Set([10,20,30]);//Set集合

for(let netry of tracking.keys()){

       console.log(netry);//输出结果:{10,20,30}

}

let data=new Map();//Map集合

data.set("第一名","张小玉");

data.set("第二名","孙大壮");

data.set("第三名","李强");

for(let entry of data.keys()){

       console.log(entry);//输出结果:{"第一名","第二名","第三名"}

}


http://www.ppmy.cn/news/1529735.html

相关文章

MySQL关卡任务书

基础任务 1. MySQL中char和varchar的区别&#xff0c;varchar&#xff08;100&#xff09;中的一百的含义&#xff0c;能存放多少汉字&#xff1f; CHAR类型用于固定长度的字符串。当存储数据时&#xff0c;会自动填充到指定长度&#xff0c;即使实际内容没有达到该长度。 VA…

Android中如何调用DLL文件

在 Android 设备上直接调用 DLL&#xff08;动态链接库&#xff09;文件是不可行的&#xff0c;因为 DLL 文件是 Windows 操作系统下的一种可执行文件格式&#xff0c;而 Android 操作系统基于 Linux 内核&#xff0c;两者在底层架构和 API 支持上存在根本差异。不过&#xff0…

软技能与AI技术的融合

一、引言 ----  随着人工智能&#xff08;AI&#xff09;和生成式人工智能&#xff08;AIGC&#xff09;如ChatGPT、Midjourney、Claude等大语言模型的迅速崛起&#xff0c;AI辅助编程工具已经变得越来越普遍。这不仅意味着程序员的工作方式正在发生深刻的变革&#xff0c;同…

[Golang] Context

[Golang] Context 文章目录 [Golang] Context什么是context创建context创建根context创建context context的作用并发控制context.WithCancelcontext.WithDeadlinecontext.WithTimeoutcontext.WithValue 什么是context Golang在1.7版本中引入了一个标准库的接口context&#xf…

Java之类,对象,构造方法

类和对象&#xff1a; 类&#xff08;Class&#xff09; 类是一个模板或蓝图&#xff0c;它定义了一组具有相同属性&#xff08;数据&#xff09;和方法&#xff08;行为&#xff09;的对象的特征。可以把类比作建筑的设计图纸&#xff0c;它描述了建筑将会有哪些部分&#x…

全方位洗衣洗鞋小程序系统,重塑干洗店服务新体验;

全方位洗衣洗鞋小程序系统&#xff0c;重塑干洗店服务新体验; 一、核心功能革新&#xff1a; 1.多元化下单模式&#xff1a;融合上门取送、到店服务、寄存网点及智能衣柜四种便捷方式&#xff0c;用户轻松一键下单&#xff0c;享受个性化服务。 2.从下单到送回&#xff0c;全程…

AWS EKS 中的负载均衡和 TLS 配置:全面指南

在现代云原生应用程序架构中,负载均衡器扮演着至关重要的角色。对于运行在 Amazon Elastic Kubernetes Service (EKS) 上的应用程序来说,理解和正确配置负载均衡是确保应用程序高可用性、可扩展性和安全性的关键。本文将全面介绍 AWS EKS 中的负载均衡配置,包括 Application…

中秋节特别游戏:给玉兔投喂月饼

&#x1f5bc;️ 效果展示 &#x1f4dc; 游戏背景 在中秋这个充满诗意的节日里&#xff0c;玉兔因为贪玩被赶下人间。在这个温柔的夜晚&#xff0c;我们希望通过一个小游戏&#xff0c;让玉兔感受到人间的温暖和关怀。&#x1f430;&#x1f319; &#x1f3ae; 游戏设计 人…