遍历器

一种接口,为各种不同的数据结构提供统一的访问机制。任何数据只要部署 了Iterator接口,就可以完成遍历操作。即被认为是可遍历的数据

iterator 的作用

  • 为各种数据结构,提供一个统一的、简便的访问接口
  • 使得数据结构的成员能够按某种次序排列
  • ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费

Iterator 的遍历过程

  1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就 是一个指针对象。
  2. 第一次调用指针对象的 next 方法,可以将指针指向数据结构的第一个成员。
  3. 第二次调用指针对象的 next 方法,指针就指向数据结构的第二个成员。
  4. 不断调用指针对象的 next 方法,直到它指向数据结构的结束位置。

可迭代对象规则

  1. 主对象/类应该存储一些数据。
  2. 主对象/类必须有全局的“众所周知的”symbol,即symbol.iterator作为它的属性, 然后按照从规则#3#6的每条规则来实现一个特有的方法。
  3. symbol.iterator 方法必须返回另一个对象 —— 一个“迭代器(iterator)”对象。
  4. “迭代器(iterator)”对象必须有一个名为 next 的方法。
  5. next方法应该可以访问存储在规则#1的数据。
  6. 如果我们调用 iteratorObj.next() ,应该返回存储在规则 #1 中的数据,如果想返回 更多的值使用格式{value:<stored data>, done: false},如果不想返回其他更多的 值则使用格式{done: true}

模拟 next()

let it = makeIterator(['a', 'b'])

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }

function makeIterator(array) {
    let nextIndex = 0
    return {
        next: function () {
            return nextIndex < array.length
                ? { value: array[nextIndex++], done: false }
                : { value: undefined, done: true }
        },
    }
}

默认的 iterator 接口

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说 ,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的 ”(iterable)。Symbol.iterator 属性本身是一个函数,就是当前数据结构默认 的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性 名Symbol.iterator,它是一个表达式,返回Symbol对象的iterator属性,这是一 个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内.

const obj = {
    [Symbol.iterator]: function () {
        return {
            next: function () {
                return {
                    value: 1,
                    done: true,
                }
            },
        }
    },
}

var a = obj[Symbol.iterator]()
a.next() // {value: 1, done: true}

// 数组原生部署了Symbol.iterator属性
let arr = ['a', 'b', 'c']
let iter = arr[Symbol.iterator]()

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

与其他遍历语法的比较

最原始的for循环

for (var index = 0; index < myArray.length; index++) {
    console.log(myArray[index])
}

数组内置的forEach

arr.forEach((i) => {
    console.log(i)
})

for...in 为遍历对象而设计

for...of循环相比上面几种做法,有一些显著的优点。

  • 有着同for...in一样的简洁语法,但是没有for...in那些缺点。
  • 不同于forEach方法,它可以与breakcontinuereturn配合使用。
  • 提供了遍历所有数据结构的统一操作接口。