用循环语句迭代数据时,需要初始化一个变量来记录每一次迭代在数据集合中的位置,使用迭代器对象返回迭代过程中集合每一个元素,可以极大简化数据操作。
迭代器
迭代器是一种特殊的对象,他有一个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对象中的每一个节点存入新的数组中。