在ES6中,有些数据结构原生具备Iterator接口(比如数组),即不用任何处理,就可以被for...of
循环遍历,有些就不行(比如对象)。原因在于,这些数据结构原生部署了Symbol.iterator
属性,另外一些数据结构没有。凡是部署了Symbol.iterator
属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
Symbol.iterator
属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器
对象(Object)之所以没有默认部署Iterator接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。
Iterator的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是ES6创造了一种新的遍历命令for...of
循环,Iterator接口主要供for...of
消费。
调用Iterator接口的场合
(1)解构赋值
对数组和Set结构进行解构赋值时,会默认调用Symbol.iterator
方法。
(2)扩展运算符(...)也会调用默认的iterator接口。
只要某个数据结构部署了Iterator接口,就可以对它使用扩展运算符,将其转为数组。扩展运算符的原理就是如此。其内部实现是Iterator接口。
在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。
我们再回头看一下Iterator接口是怎么实现的?
(3)由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。
- for...of
- Array.from()
- Map(), Set(), WeakMap(), WeakSet()(比如
new Map([['a',1],['b',2]])
) - Promise.all()
- Promise.race()
下图中如果取值的话,就是it.next().value,即可。这是不是有点像Array.from 传入一个类数组,把其转为真正的数组的做法?
for...of
循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟for...in
循环也不一样。
在ES6中,有三类数据结构原生具备Iterator接口:
1)数组、
2)某些类似数组的对象(DOM NodeList对象 和 arguments
)这俩类数组可以直接for of遍历
3)Set和Map结构。
并不是所有类似数组的对象都具有iterator接口:解决方法:
1--对于类似数组的对象(存在数值键名和length属性),部署Iterator接口,有一个简便方法,就是Symbol.iterator
方法直接引用数组的Iterator接口。
2- 使用Array.from方法将其转为数组。
对于普通的对象,for...of
结构不能直接使用,会报错,必须部署了iterator接口后才能使用。但是,这样情况下,for...in
循环依然可以用来遍历键名。关于对象想要用for of 遍历:
1)用Object.keys / values / entrys 返回一个数组,然后再用for of
2)方便的方法是将数组的Symbol.iterator
属性,直接赋值给其他对象的Symbol.iterator
属性
比如,想要让for...of
环遍历jQuery对象,只要加上下面这一行就可以了。
forEach和for of遍历数组比较:
for of比着forEach最大的优点就是可以return , break continue。
for in 和for of比较:
1.for in 是遍历对象的,遍历数组时候遍历的是索引;
2.for in 会把可枚举和不可枚举的都遍历出来,for of不会
3.某些情况下,for in遍历出来的键名不会按顺序,for of是有序的。