ES6增加了Map,Set数据结构而且还实现了迭代器机制(Iterator),这是多么令人兴奋的事情。
迭代器(Iterator)模式,又叫做游标(Cursor)模式。GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。 从定义可见,迭代器模式是为容器而生。很明显,对容器对象的访问必然涉及到遍历算法。你可以一股脑的将遍历方法塞到容器对象中去;或者根本不去提供什么遍历算法,让使用容器的人自己去实现去吧。这两种情况好像都能够解决问题。(取自百度百科,发现百度的名词解释比维基棒 :P )
即Iterator是为了遍历容器(set , map , array,object)而提供的接口
下面我会从几个方面来阐述JavaScript的迭代器:
0x00 Iterator实现原理及使用
0x01 对JavaScript的循环方法进行汇总
0x00 Iterator原理及使用姿势
实际上遍历的原理大家都能想得到。
抽象数据类型(Abstract Data Type ADT)是一个数据模型以及包含对数据的操作,定义并实现了抽象数据模型就可以像使用基本数据类型那样。
数据结构是实现抽象数据类型的算法。首先创建我们需要一个类似指针☞的对象(下面称为指针对象),记下当前抽象数据类型起点(数组,对象等可遍历数据类型,阮老师这里写的是数据结构,个人认为ADT更合适),而且返回格式为 { value : “XXX” , done : false } ,当遍历结束则返回 {value : undefined , done : true} 。
最重要的是为这个指针对象添加next( )方法(有没觉得很熟悉,数据结构课程我们经常遇到),当我们第一次调用next( )指针就指向ADT第一个成员。相应的,第二次调用就指向第二个成员。
然后不断地调用next( ) ,就一直遍历下去直到指向ADT结束的位置。
还有很重要的一点(“敲黑板” ),ES6规定只有具有Symbol.iterator属性的ADT才具备可遍历性,而且使用Iterator之前需要调用Symbol.iterator方法(摊手.jpg),其实知道了原理我们也能写个Iterator接口玩玩。
数组,类数组对象,Set和Map内置Iterator接口,使用方法如下let arr = [ “1” , “2” , “3”]
let it = arr[Symbol.iterator] ( )
it.next( ) // { value: “1” , done: false}
it.next( ) // { value: “2” , done: false}
it.next( ) // { value: “3” ,done: false}
it.next( ) // { value: undefined , done: true}ES6引用了for … of ,只要具有Symbol.iterator属性就可以用for…of 来遍历,即for…of 循环内部调用的是Iterator接口方法。
let arr = [“1”,”2”,”3”]
for (let v of arr){
console.log(v)
}let s = new Set([“1”,”2”,”3”])
for (let a of s){
console.log(a)
}另外解构赋值,扩展运算符也是默认调用Iterator接口
既然有引入了for…of,而且许多ADT默认调用Iterator接口,而且使用 it.next( )方式来遍历还不如 for…of来的简单,暴力,这样看起来it.next( )遍历方式貌似有点弱了。
显然这是不对的。
(华丽的分割线)
ES6提供了Generator函数来解决异步编程,当Generator遇上Iterator产生的化学反应——状态机。(Generator需要较长的篇幅来讲解,会专门写篇关于异步编程博客,这里就不深究了)
0x01 对JavaScript所有迭代的方法进行汇总
for循环是最常见的一种写法,大家都很熟悉了。
for-in是用来遍历的是对象的属性(可枚举属性),而在JavaScript中Array可以看成特殊的对象,因此使用for-in遍历数组得到的是数组的索引。在使用for-in的时候,会遍历原型链,所以在一些场景下for的性能高于for-in。因为for-in会遍历原型链,而且forEach()不支持break和return,所以ES6新增了for-of ,解决了这些问题,而且ES6新增的set,map等数据结构只要内置有Iterator的通通都可以使用 “for-of” 来遍历数据。
最后,以下是ES5为数组定义了5个迭代方法。
- every
- filter
- forEach
- map
- some
every( )
对数组每一项执行回调函数,如果该函数对每一项都返回true则返回true。
![]()
some( ) # 跟every( )有些差别,但使用场景差不多
对数组每一项执行回调函数,如果该函数对任一项返回true则返回true。
![]()
filter( ) # 起到过滤功能
对数组每一项执行回调函数,返回该函数会返回true的项组成的数组
![]()
forEach( )
对数组每一项执行回调函数,没有返回值
(”敲黑板”,考试重点)☞ forEach循环中,break或return不能跳出循环
![]()
map( )
对数组每一项执行回调函数,返回所有执行结果组成的数组。
![]()
本文参考《JavaScript高级程序设计》以及阮一峰老师的《ES6标准入门》