用循环语句迭代数据时,必须要初始化一个变量来记录每一次迭代在数据集合中的位置,迭代器的使用可以极大的简化数据操作。
什么是迭代器Iterator
迭代器是一种特殊的对象,具有专门的接口,所有迭代器对象都有一个next方法,每次调用都返回一个结果对象。
结果对象有两个属性:一个是value
,表示下一个将要返回的值;另一个是done
,是一个布尔值,当没有更多可返回数据时返回true。
迭代器还会保存一个内部指针,用来指向当前集合中值的位置,没调用一次next()
方法,都会返回下一个可用的值。
如果最后一个值返回后再调用next()
方法,返回的对象中属性done
为true,value
则包含迭代器最终返回的值,这个返回值不是数据集的一部分,与函数的返回值类似,是函数调用过程中最后一次给调用者传递信息的方法,如果没有相关数据则返回undefined
。
1 | // ES5 语法创建一个迭代器 |
什么是生成器Generator
生成器是一种返回迭代器的函数,通过function
关键字后的星号(*
)来表示,函数中会用到新的关键字yield
。星号可以紧挨着function
关键字,也可以在中间添加一个空格:
1 | function *createIterator() { |
yield
关键字也是ES6的新特性,可以通过它来指定调用迭代器的next()
方法时的返回值及返回顺序。
生成器函数最有序的部分大概是:每当执行完一条yield
语句后函数就会自动停止执行,直到再次调用迭代器的next()
方法才会继续执行下一个yield
语句。
使用yield
关键字可以返回任何值或表达式,因此可以通过生成器函数批量的给迭代器添加元素。
1 | function *createIterator(items) { |
yield的使用限制
yield关键字只可在生成器内部使用,在其它地方使用会导致程序抛出语法错误,即便在生成器内部的函数里使用也是如此。常见案例:
1 | function *createIterator(items) { |
可迭代对象和for-of
循环
可迭代对象具有Symbol.iterator
属性,是一种与迭代器密切相关的对象。Symbol.iterator
通过指定的函数可以返回一个作用于附属对象的迭代器。在ES6中,所有的集合对象(数组、Set
集合及Map
集合)和字符串都是可迭代对象,这些对象中都有默认的迭代器。
for-of
循环每执行一次都会调用可迭代对象的next()
方法,并将迭代器返回的结果对象的value
属性存储在一个变量中,循环将持续执行这一过程直到返回对象的done
属性的值为true。
访问默认迭代器
可以通过Symbol.iterator
来访问对象默认的迭代器。
由于具有Symbol.iterator
属性的对象都有默认的迭代器,因此可以用它来检测对象是否为可迭代对象:
1 | function isIterable(object) { |
创建可迭代对象
默认情况下,开发者定义的对象都是不可迭代对象,但如果给Symbol.iterator
属性添加一个生成器,则可以将其变为可迭代对象:
1 | let collection = { |
内建迭代器
在ES6中有3中类型但集合对象:数组、Map
集合与Set
集合。为了更好的访问对象中的内容,这3种对象都内建来三种迭代器:
- entries()
- values()
- keys()
entries()迭代器(TODO)
values()迭代器(TODO)
keys()迭代器(TODO)
字符串迭代器
由于方括号操作的是编码单元而非字符,因此无法正确访问双字节字符。由于双字节字符被视作两个独立的编码单元,在使用方括号获取双字节字符时得到的是两个空。
所幸,ES6的目标是全面支持Unicode,并且我们可以通过改变字符串的默认迭代器来解决这个问题,使其操作字符而不是编码单元。
NodeList
迭代器
自从ES6添加了默认迭代器后,DOM定义中的NodeList
类型(定义在HTML标准而不是ES6标准中)也拥有了默认迭代器,其行为与数组的默认迭代器完全一致。所以可以将NodeList
应用于for-of
循环及其他支持对象默认迭代器的地方。
展开运算符与非数组可迭代对象
由于展开运算符可以作用于任意可迭代对象,因此如果想将可迭代对象转换为数组,这是最简单的方法。你既可以将字符串中的每一个字符(不是编码单元)存入新数组中,也可以将浏览器中的NodeList
对象中的每一个节点存入新数组中。
1 | let set = new Set([1, 2, 3]), |