用循环语句迭代数据时,需要初始化一个变量来记录每一次迭代在数据集合中的位置,使用迭代器对象返回迭代过程中集合每一个元素,可以极大简化数据操作。
迭代器
迭代器是一种特殊的对象,他有一个next()方法,每次调用都返回一个结果对象。结果对象有两个属性,一个是value,表示下一个将要返回的值,一个是done,他是一个布尔类型的值,当没有更多可返回的数据时返回true·。迭代器还会保存一个内部指针,用来指向当前集合中的位置,每调用一次next()方法,都会返回下一个可用的值。
如果在最后一个值返回后再调用next()方法,返回对象中属性done的值为true,属性value的值为包含迭代器最终返回的值,这个值不是数据集的一部分,如果没有相关数据则返回undefined。
下面用ES5来模拟一个迭代器
生成器
生成器是一种返回迭代器的函数,通过function关键字后的星号(*)来表示,星号可以紧挨着function关键字,也可以在中间添加一个空格。函数中用到关键词yield,可以通过它来指定调用迭代器的next()方法时的返回值及返回顺序。
每当执行完一条yield语句后,函数就会自动停止执行。每次调用next()方法,函数会继续执行并执行下一条yield语句。上面代码中,执行完yield 1语句之后,函数便不在执行其他语句,直到再次调用迭代器的next()方法才会继续执行yield 2语句。使用yield关键字可以返回任何值或表达式,所以可通过生成器函数批量地给迭代器添加元素。
|
|
yield关键字只可在生成器内部使用,在其他地方使用会导致程序抛出语法错误,即使在生成器内部的函数里使用也是如此。比如上例生成器内部改为items.forEach(i => {yield i});会抛出错误。
- 生成器表达式
可以通过函数表达式来创建生成器,在function关键字和小括号中间添加一个星号(*)即可12345let createIterator = function *(items){for(let i = 0; i < items.length; i++){yield items[i]}}
不能用箭头函数来创建生成器
- 生成器对象的方法
由于生成器本身是就是函数,可以将它们添加到对象中。123456789101112let o = {createIterator: function *(items){for(let i = 0; i < items.length; i++){yield items[i]}}//ES6函数方法的简写方式// *createIterator(items){//...// }}let iterator = o.createIterator([2,4,5]);
可迭代对象和for...of循环
可迭代对象具有Symbol.iterator属性,该属性是一个生成器函数。Symbol.iterator通过指定的函数可以返回一个作用于附属对象的迭代器。ES6中,所有集合对象(数组、TypedArray、Set集合及Map集合)和字符串都是可迭代对象,这些对象中都有默认的迭代器。
生成器默认会为
Symbol.iterator属性赋值,通过生成器创建的迭代器都是可迭代对象。
for...of循环需要用到可迭代对象的功能,循环每执行一次都会调用可迭代对象的next()方法,并将迭代器返回结果对象的value属性存储在一个变量中,循环遇到返回结果对象done属性的值为undefined时终止。
for...of循环通过调用数组arr的Symbol.iterator方法来获取迭代器,这一过程是在JavaScript引擎中完成的。随后迭代器的next()方法别多次调用,从其返回对象的value属性读取值并存储在变量num中,当结果对象done属性值为true时循环退出,所以num不会被赋值undefined。
for...of语句用于不可迭代对象、null或undefined将会导致程序抛出错误。
- 访问默认迭代器
可以通过Symbol.iterator来访问对象默认的迭代器
具有Symbol.iterator属性的对象都有默认的迭代器,可以用它来检测对象是否为可迭代对象。
- 创建可迭代对象
默认情况下,开发者定义的对象是不可迭代对象,可以给Symbol.iterator属性添加一个生成器,这样就可以将其变成可迭代对象。
先创建一个生成器(星号仍然在属性名前)并将其赋值给对象的Symbol.iterator属性来创建默认的迭代器。生成器中通过for-of循环迭代this.items并用yield返回每一个值。
内建迭代器
ES6中,已经为许多内建类型提供了内建迭代器。
集合对象迭代器
ES6中有3种类型的集合对象:数组、Map集合与Set集合。这3种对象都内建了以下3种迭代器
entries()返回一个迭代器,其值为多个键值对。values()返回一个迭代器,其值为集合的值。keys()返回一个迭代器,其值为集合中所有键名。
entries()
每次调用next()方法时,entries()迭代器都会返回一个数组,数组的两个元素分别表示集合中的每个元素的键与值。如果是Set集合,第一个元素和第二个元素都是值。
values()
调用values()迭代器会返回集合中所有的值
keys()
keys()返回集合中存在的每一个键,如果是Set集合,由于键和值是相同的,所以keys()和values()返回的也是相同的迭代器。
默认迭代器
每个集合类型都有一个默认的迭代器,在for...of循环中,没有显示指定则使用默认迭代器。数组(包括TypedArray)和Set集合默认迭代器是values()方法,Map集合默认迭代器是entries()。
字符串迭代器
ES5规定,字符串可以通过方括号访问字符,由于方括号操作的是编码单元而不是字符,所以无法正确访问双字节字符。
可以通过字符串的默认迭代器来解决这个问题,其操作的是字符而不是编码单元。
NodeList迭代器
DOM标准中有一额NodeList类型,document对象中的所有元素都用这个类型来表示。ES6添加了默认迭代器后,NodeList也有默认迭代器表现与数组一致。
展开运算符与非数组可迭代对象
展开运算符可以操作所有可迭代对象,根据默认迭代器来选取要引用的值,从迭代器读取所有值。
将一个可迭代对象转成转化为数组,这是最简单的方法。可以将字符串中的每一个字符存入新数组,也可以将NodeList对象中的每一个节点存入新的数组中。