Iterator遍历器
任何数据结构只要部署 Iterator
接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
和Generator函数返回的遍历器一样,都是调用next方法,返回一个具有value和done两个属性的对象。
遍历器并不依赖于他所遍历的哪个数据结构,这也就可以用遍历器模拟数据结构。
当使用for...of
循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口
一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)
默认的 Iterator 接口部署在数据结构的Symbol.iterator
属性
原生具备 Iterator 接口的数据结构如下。
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
一个对象如果要具备可被for...of
循环调用的 Iterator 接口,就必须在Symbol.iterator
的属性上部署遍历器生成方法(原型链上的对象具有该方法也可),也可以使用while
循环遍历。
对于类似数组的对象(存在数值键名和length属性),部署 Iterator 接口,有一个简便方法,就是Symbol.iterator
方法直接引用数组的 Iterator 接口。
普通对象部署数组的Symbol.iterator
方法,并无效果
如果Symbol.iterator
方法对应的不是遍历器生成函数(即会返回一个遍历器对象),解释引擎将会报错。
字符串是一个类似数组的对象,也原生具有 Iterator 接口
Symbol.iterator
方法的最简单实现是Generator
函数
调用 Iterator 接口的场合
-
对数组和 Set 结构进行解构赋值时,会默认调用
Symbol.iterator
方法。 -
扩展运算符(
...
)也会调用默认的 Iterator 接口。 -
yield*
后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口 -
for...of
-
Array.from()
-
Map(), Set(), WeakMap(), WeakSet()(比如
new Map([['a',1],['b',2]])
) -
Promise.all()
-
Promise.race()
遍历器对象的 return(),throw()
next
方法是必须部署的,return
方法和throw
方法是否部署是可选的。
但使用for...of
的时候,循环体内使用了break
或者throw
,或者是出错了,都会自动调用return
方法
return
方法必须返回一个对象,这是 Generator 规格决定的。
throw
方法主要是配合 Generator 函数使用,一般的遍历器对象用不到这个方法
for...of 循环
for...of
循环内部调用的是数据结构的Symbol.iterator
方法
JavaScript 原有的for...in
循环,只能获得对象的键名,不能直接获取键值。ES6 提供for...of
循环,默认遍历获得键值。
for...of
遍历数组的时候与for...in
不一样,只能遍历数字索引的属性,在数组中的自定义属性是不会被遍历的。
Set 结构遍历时,返回的是一个值,而 Map 结构遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员的键名和键值。
对于字符串来说,for...of
循环还有一个特点,就是会正确识别 32 位 UTF-16 字符。
并不是所有类似数组的对象都具有 Iterator 接口,一个简便的解决方法,就是使用Array.from
方法将其转为数组。
普通的对象,不能直接使用for...of
结构,可以使用Object.keys()
,或者使用Generator
函数包装。
与其他遍历器的比较
forEach
循环无法中途跳出,break
命令或return
命令都不能奏效。
for...in
循环有几个缺点:
- 键名是数字类型,但是遍历出来却是字符串类型
- 遍历数组的时候不是数字的键名也会被遍历
- 某些情况下,
for...in
循环会以任意顺序遍历键名。
综合上面的缺点,就是for...of
的优点了。