javascriptRemke之深入迭代
javascriptRemke之深入迭代
前言:"迭代"意为按照顺序反复多次执行一段程序,ECMAscript6中新增了两个高级特性:迭代器与生成器,使用这两个特性能更高效地实现迭代,本文主要围绕迭代器详细展开叙述。
一、何为迭代
迭代即重复执行某段程序,最简单的一种迭代即计数循环。
1 for(let i =0;i<10;i++){
2 console.log(i);
3 }
迭代会在一个有序集合上进行,数组是JavaScript中最经典的例子,数组每一项可以通过索引获取,所以整个数组都可以通过递增索引来遍历,但是很多时候使用数组进行循环并不理想。原因如下:
- 迭代之前需要事先知道如何使用数据结构。在前后端实际开发中并不能完全适用于所有数据结构,前端通常得根据后端得接口文档进行数据处理,数组的遍历有时并不能满足需求。
- 遍历顺序可能非数据结构固有的。通过递增索引来访问数据是特定于数组类型的方式,并不适用于其他隐式顺序的数据结构。
ES5中为实现通用迭代需求,新增了Array.prototype.forEach()方法,但是效果仍不理想。这个方法实现了单独记录索引(传入forEach作为参数的回调函数的第一个参数)并能通过数组对象取得值,但是仍未解决标识迭代何时中止的问题,因此这个方法也只适用于数组,且回调形式较为繁琐。因此,ES6中采用迭代器模式作为解决方案,让开发者无须实现知道如何迭代就能实现迭代操作。
1 let arr = ['sunwukong','zhubajie','shaheshang'];
2 arr.forEach((value,index,obj)=>{
3 console.log(value)
4 })
5 //sunwukong
6 //zhubajie
7 //shaheshang
二、迭代器模式
2.1 可迭代对象
迭代器模式描述了一个方案,把具有迭代能力的结构称为"可迭代对象"。可迭代对象可以理解为数组或集合(Set)类型的对象。它们拥有以下特性:
- 包含有限个元素
- 具有无歧义的遍历顺序
计数循环和数组都具有可迭代对象的行为。
2.2 迭代器
迭代器模式中还有一个关键概念"迭代器",迭代器是按需创建的一次性对象,每个迭代器都会关联一个可迭代对象,且迭代器会暴露可迭代对象的相关API。
当使用可以接收可迭代对象的原生语言(for-of、Map、Set...)时,会创建一个迭代器,从而实现迭代。
*迭代器模式将可迭代对象与迭代器分离,实现了迭代器无须了解与之关联的可迭代对象的结构,只需要知道如何取得连续的值,这也正式分离可迭代对象与迭代器的强大之处。
2.3 可迭代协议
实现可迭代协议要求具备两种能力:
- 支持迭代的自我识别能力
- 创建接口的能力
要创建接口,就必须要暴露一个属性作为默认迭代器且这个属性必须要用特殊的Symbol.iterator作为建。默认迭代器属性必须引用一个迭代器工厂函数,通过调用这个工厂函数返回一个新的迭代器,简单来说,只有当对象拥有Symbol.iterator这一属性,这一对象才可以实现迭代。
1 let str = 'abc';
2 console.log(str[Symbol.iterator]());// f value(){native code};
3 let obj = {};
4 console.log(obj[Symbol.iterator]());//报错
5 console.log(obj.__proto__.hasOwnProperty(Symbol.iterator));
6 //false
7 console.log(arr.__proto__.hasOwnProperty(Symbol.iterator));
8 //true
能够实现接口功能的内置属性有以下几类:
- 字符串
- 数组
- 映射(Map)
- 集合(Set)
- arguments对象(伪数组)
- NodeList等DOM集合(document.queryselectorAll()、document.getElementsByClassName()...)
能够接收可迭代对象的原始语言包括:
- for-of
- 数组循环
- 扩展运算符
- Array.from()
- 创建集合
- 创建映射
- Promise.all()
- Promise.race()
- yield*操作符
这些原始语言会在后台调用提供的可迭代对象的这个工厂函数,从而创建一个迭代器。
三、迭代器协议
迭代器是一种一次性使用的对象,在使用能够接收可迭代对象的原始语言时会临时创建,用于迭代与其关联的可迭代对象。
迭代器API使用next()方法在可迭代对象中遍历数据,每次成功调用next(),都会返回一个IteratorResult对象,这一对象包含迭代器返回的下一个值(value)和状态(done)。若不调用next()方法则无法知道迭代器的当前位置,因此next()是迭代器中必须存在的方法,不然仍然无法成为迭代器。
next()返回对象包含两个属性:done和value,done是一个布尔值,表示next()是否还可以再次调用并取得下一个有效值,为false表示状态仍未耗尽,还可以获取下一有效值,为true时则下次迭代返回的value为undefined。value包含可迭代对象的下一个值。
1 //可迭代对象
2 let arr = ['Billy','Jack'];
3 //迭代器工厂函数
4 console.log(arr[Symbol.iterator]); //f values(){native code};
5 //创建一个迭代器实例
6 let iterator = arr[Symbol.iterator()]();
7 console.log(iterator) //ArrayIterator();
8 //执行迭代
9 console.log(iterator.next()); //{done:false,value:'Billy'};
10 console.log(iterator.next()); //{done:false,value:'Jack'};
11 console.log(iterator.next()); //{done:true,value:undefined};
注意:
- 不同迭代器实例相互之间没有关系,只会独立遍历可迭代对象。
- 迭代器并不与可迭代对象某个时刻的快照绑定。即中途插入新元素,使用next()进行迭代会展现新元素。
四、自定义迭代器
任何使用Iterator接口的对象都可以作为迭代器使用。
下方通过实现Iterator接口完成自定义迭代器。
1 class IteratorTest{
2 constructor(limit){
3 this.limit = limit;
4 this.present = 1;
5 }
6 [Symbol.iterator](){
7 return this;
8 }
9 next(){
10 if(this.present<this.limit){
11 return{
12 done:false,value:this.present++
13 }
14 }
15 else{
16 return{
17 done:true,value:undefined
18 }
19 }
20 }
21 }
22 const iterator = new IteratorTest(5)
23 for (let item of iterator) {
24 console.log(item)
25 }
26 //1
27 //2
28 //3
29 //4
上方例子在for-of的作用下调用Symbol.iterator属性返回自身实例(创建迭代器),接着调用实例原型上的next()方法实现迭代。
但是上方例子存在的缺陷为每个实例之恩被迭代一次。若多次遍历不打印结果。
为了能让可迭代对象能够实现正常迭代需求,因此每创建一个迭代器就对应一个新迭代器,采用的解决方案为将计数变量放在闭包里,然后通过闭包返回迭代器。
1 class IteratorTest{
2 constructor(limit){
3 this.limit = limit;
4 }
5 [Symbol.iterator](){
6 let start = 1;
7 let present = start;
8 let limit = this.limit;
9 return{
10 next(){
11 if(present<limit){
12 return{
13 done:false,value:present++
14 }
15 }
16 else{
17 return{
18 done:true,value:undefined
19 }
20 }
21 }
22 }
23 }
24 }
25 const iterator = new IteratorTest(5)
26 for (let item of iterator) {
27 console.log(item)
28 }
29 for (let item of iterator) {
30 console.log(item)
31 }
32 //1
33 //2
34 //3
35 //4
36 //1
37 //2
38 //3
39 //4
上面例子因为每个迭代器都实现了Iterator接口且都有next()方法,所以在任何期待可迭代对象的地方都可以循环。
五、提前中止迭代器
为提前中止迭代器,需要return()方法返回一个用于提前中止迭代的有效对象(IteratorResult),这个对象可以只返回{done:true}
return()方法用于指定迭代器在提前关闭时执行的逻辑。提前关闭迭代器的可能情况为:
- for-of循环通过break、continue、return或throw提前退出。
- 解构操作并未消费所有值。
1 class IteratorTest{
2 constructor(limit){
3 this.limit = limit;
4 }
5 [Symbol.iterator](){
6 let start = 1;
7 let present = start;
8 let limit = this.limit;
9 return{
10 next(){
11 if(present<limit){
12 return{
13 done:false,value:present++
14 }
15 }
16 else{
17 return{
18 done:true,value:undefined
19 }
20 }
21 },
22 return(){
23 console.log('exit');
24 return {done:true};
25 }
26 }
27 }
28 }
29 const iterator = new IteratorTest(5)
30 for (let item of iterator) {
31 if(item>2){
32 break;
33 }
34 console.log(item);
35 }
36 //1
37 //2
38 //exit
注意:如果迭代器没有关闭,则还可以继续从上次离开的地方继续迭代。
1 let arr = [1,2,3,4,5];
2 const iterator2 = arr[Symbol.iterator]();
3 for(let i of iterator2){
4 console.log(i);
5 if(i>2){
6 break
7 }
8 }
9 //1
10 //2
11 //3
12 for(let i of iterator2){
13 console.log(i);
14 }
15 //4
16 //5
javascriptRemke之深入迭代的更多相关文章
- javascript中的Array对象 —— 数组的合并、转换、迭代、排序、堆栈
Array 是javascript中经常用到的数据类型.javascript 的数组其他语言中数组的最大的区别是其每个数组项都可以保存任何类型的数据.本文主要讨论javascript中数组的声明.转换 ...
- IteratorPattern(迭代子模式)
/** * 迭代子模式 * @author TMAC-J * 聚合:某一类对象的集合 * 迭代:行为方式,用来处理聚合 * 是一种行为模式,用于将聚合本身和操作聚合的行为分离 * Java中的COLL ...
- JavaScript高级程序设计--对象,数组(栈方法,队列方法,重排序方法,迭代方法)
1.使用对象字面量定义对象 var person={}; 使用这种方式创建对象时,实际上不会调用Object构造函数. 开发人员更喜欢对象字面量的语法. 2.有时候需要传递大量可选参数的情形时,一 ...
- python征程3.1(列表,迭代,函数,dic,set,的简单应用)
1.列表的切片. 1.对list进行切片.'''name=["wangshuai","wangchuan","wangjingliang", ...
- 让产品有效迭代,前端A/B Testing的简单实现
A/B Testing简介 互联网产品的迭代速度很快,往往一周一小发布,一月一大发布,产品提出的种种需求,哪些改动是提升产品体验的,哪些是阻碍产品进步的,如果没有数据可以参考,仅仅是靠拍脑袋的话,对产 ...
- JAVA中的for-each循环与迭代
在学习java中的collection时注意到,collection层次的根接口Collection实现了Iterable<T>接口(位于java.lang包中),实现这个接口允许对象成为 ...
- 迭代字典中的key和value
字典是python中十分重要的一个内容. 今天我们来谈谈,在一个 for 循环中,能否同时迭代 key和value?当然可以咯. dict 对象的 items() 方法返回的值: >>&g ...
- python中的迭代、生成器等等
本人对编程语言实在是一窍不通啊...今天看了廖雪峰老师的关于迭代,迭代器,生成器,递归等等,word天,这都什么跟什么啊... 1.关于迭代 如果给定一个list或tuple,我们可以通过for循环来 ...
- ICP算法(Iterative Closest Point迭代最近点算法)
标签: 图像匹配ICP算法机器视觉 2015-12-01 21:09 2217人阅读 评论(0) 收藏 举报 分类: Computer Vision(27) 版权声明:本文为博主原创文章,未经博主允许 ...
随机推荐
- Consul 入门-运行
HashiCorp Consul 是由 HashiCorp 公司开发的,它是一家专注于 DevOps 工具链的公司,旗下的明星级产品包括 Vagrant.Terraform.Vault.Nomad 以 ...
- Linux的基础——虚拟机的克隆
1.虚拟机的安装 虚拟机的安装在另一个文档 安装jdk(在另外一个文档中) 2.虚拟机的克隆 准备工作:一台装有Linux系统的主机(已经配置好jdk) 选择主机进行克隆 注意:这里一定要选择创建完整 ...
- Kickstart部署多系统
原文转自:https://www.cnblogs.com/itzgr/p/10029587.html作者:木二 目录 一 准备 1.1 完整架构:Kickstart+DHCP+HTTP+TFTP+PX ...
- Java最大栈深度有多大?-从一道面试题开始学习JVM
一.问题:Java最大支持栈深度有多大? 1.分析 有JVM的内存结构我们可知: 随着线程栈的大小越大,能够支持越多的方法调用,也即是能够存储更多的栈帧: 局部变量表内容越多,那么栈帧就越大,栈深度就 ...
- jq的选择器中带有特殊符号无法获取元素
因项目需要,将元素id命名为数组(array[i].string) 使用jq去获取该id的元素时,返回的是个undefined.即jq获取不到该元素,因为该元素中的id含有特殊字符"[&qu ...
- (4)java Spring Cloud+Spring boot+mybatis企业快速开发架构之SpringCloud-Spring Cloud开发环境的准备和Lombok安装步骤
开发环境的准备主要涉及三个方面:JDK.Maven.Spring Tools 4 for Eclipse. 1.JDK JDK 的版本用 1.8 即可,环境变量大家自行去配置.配置好环境变量,在命 ...
- Java中HashCode()和equals()的作用
引言 我们知道Java中的集合(Collection)大致可以分为两类,一类是List,再有一类是Set. 前者集合内的元素是有序的,元素可以重复:后者元素无序,但元素不可重复. 这里就引出一个问题: ...
- python中时间处理标准库DateTime加强版库:pendulum
DateTime 的时区问题 Python的datetime可以处理2种类型的时间,分别为offset-naive和offset-aware.前者是指没有包含时区信息的时间,后者是指包含时区信息的时间 ...
- PHP网上商城
页面展示: 代码参考:Php实现简易购物商城系统 - 邵文 - 博客园 (cnblogs.com)
- .NET 中的HTTP 3支持
dotnet团队官方博客发布了一篇HTTP3的文章:HTTP/3 support in .NET 6.文章介绍了.NET 6 将预览支持HTTP3,.NET 7正式支持HTTP3,原因主要是HTTP/ ...