Iterator 概念

遍历器是一种接口,为各种不同的数据结构提供统一的访问机制。
作用:

  • 为各种数据结构,提供一个统一的、简便的访问接口
  • 使数据结构成员能够按某种次序排列
  • 主要供 for…of 消费

遍历过程:

  • 创建一个指针对象
  • 第一次调用指针对象的next方法,指针指向数据结构的第一个成员
  • 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员
  • 不断调用指针对象的next方法,知道它指向数据结构的结束位置

每次调用next方法,返回一个包含value 和 done 两个属性的对象。

默认Iterator 接口

一种数据结构只要部署了 Iterator 接口,就是可遍历的。
Es6,默认的Iterator 接口部署在数据结构的Symbol.iterator 属性。
原生具备Iterator接口的数据结构:

  • -Array
  • -Map
  • -Set
  • -String
  • -TypedArray
  • -函数的arguments对象
  • -NodeList对象

调用Iterator 接口的场合

以下场景会默认调用Iterator接口(即Symbol.iterator方法)

解构赋值

对数组和Set结构赋值时,会默认调用Symbole.iterator 方法

  1. let set = new Set()
  2. set.add(1)
  3. set.add(2)
  4. set.add(3)
  5. let [x, y] = set

扩展运算符

扩展运算符也会调用默认的 Iterator 接口

  1. let str = 'hello'
  2. [...str]

yield*

yield* 后面跟一个可遍历的结构,它会调用该结构的遍历器接口

  1. let generator = function* () {
  2. yield 1;
  3. yield* [2, 3, 4];
  4. yield 5;
  5. }
  6. let generatorIterator = generator()
  7. console.log(generatorIterator.next())
  8. console.log(generatorIterator.next())
  9. console.log(generatorIterator.next())
  10. console.log(generatorIterator.next())
  11. console.log(generatorIterator.next())

其他场合

由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,都可以调用遍历器接口。
-for…of
-Array.from()
-Map(), Set(), WeakMap(), WeakSet()
-Promise.all()
-Promise.race()

字符串的Iterator接口

字符串是一个类似数组的对象,也原生具有Iterator 接口。

  1. var someString = 'hi'
  2. var strIter = someString[Symbol.iterator]()
  3. console.log(strIter.next())
  4. console.log(strIter.next())
  5. console.log(strIter.next())

可以覆盖原生的Symbol.iterator 方法,达到修改遍历器行为的目的。

  1. var objStr = new String('hello')
  2. console.log([...objStr])
  3. objStr[Symbol.iterator] = function() {
  4. return {
  5. next: function() {
  6. if (this._first) {
  7. this._first = false
  8. return { value: 'bye', done: false }
  9. } else {
  10. return { done: true }
  11. }
  12. },
  13. _first: true
  14. }
  15. }
  16. console.log([...objStr]) // ['bye']
  17. console.log(objStr) // {'hello'}

Iterator 接口 与 Generator 函数

  1. let myIterable = {
  2. * [Symbol.iterator]() {
  3. yield 1;
  4. yield 2;
  5. yield 3;
  6. yield 4;
  7. }
  8. }
  9. console.log([...myIterable])

遍历器对象的 return(), throw()

for…of 循环

数组

  1. const arr = ['red', 'green', 'blue']
  2. for (let v of arr) {
  3. console.log(v)
  4. }
  5. // 遍历数组索引
  6. for (let key of Object.keys(arr)) {
  7. console.log(key)
  8. }
  9. for (let [key, value] of Ojbect.entries(arr)) {
  10. console.log(key, value)
  11. }

Set 和 Map 结构

Set 和 Map 结构也原声具有Iterator 接口

  1. let engines = new Set(['Gecko', 'Trident', 'Webkit', 'webkit'])
  2. for (let e of engines) {
  3. console.log(e)
  4. }
  5. let es6 = new Map()
  6. es6.set('edition', 6)
  7. es6.set('committee', 'Tc39')
  8. for (let [key, value] of es6) {
  9. console.log(key + ' : ' + value)
  10. }

对象

  1. for (let key of Object.keys(somObject)) {
  2. console.log(key+': ' + someObject[key])
  3. }
  1. // 使用Generator 函数将对象重新包装一下
  2. function* entries(obj) {
  3. for (let key of Object.keys(obj)) {
  4. yield [key, Obj[key]]
  5. }
  6. }
  7. for (let [key, value] of entries(obj)) {
  8. console.log(key, '-> ', value)
  9. }

与其他遍历语法的比较

  1. // for 循环
  2. for (let index = 0; index < myArray.length; index++) {
  3. console.log(myArray[index])
  4. }
  5. // forEach 方法
  6. myArray.forEach((value) => {
  7. console.log(value)
  8. })
  9. --forEach, 无法中途跳出forEach 循环, breakreturn 都不能奏效
  10. // for...in 循环
  11. for (let index in myArray) {
  12. console.log(myArray[index])
  13. }
  14. -----for...in缺点,不适用于遍历数组
  15. -数组的键名是数字
  16. -还会遍历手动添加的其他键,包括原型链上的键
  17. -某些情况下,for...in循环会以任意顺序遍历键名
  18. // for...of 循环优点:
  19. let iteArr = [10, 30, 60, 70]
  20. for (let n of iteArr) {
  21. if (n > 50) {
  22. break
  23. }
  24. console.log(n)
  25. }
  26. -有着同for...in 一样简洁语法,但是没有for...in那些缺点
  27. -不用于forEach,它可以与breakcontinuereturn配合使用
  28. -提供了遍历所有数据结构的统一操作接口