ES6读书简记·Iterator & for...of

CY 2019年01月10日 642次浏览

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的优点了。