简单学习遍历器Iterator

遍历器iterator

ES6之后数据结构除了数组和对象,又添加了Map和Set。遍历器是一种接口规格,任何数据结构只要部署这个接口,就可以完成遍历操作

提供了一个指针,通过指针的指向进行遍历操作

它提供了一个指针,默认指向当前数据结构的起始位置。也就是说,遍历器返回一个内部指针,
第一次调用next方法,将指针指向第一个成员,第二次调用next方法,指针指向第二个成员

下面是一个模拟遍历器指针的例子

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

let it = makeIterator(['a', 'b'])
it.next() // {value: 'a', done: false}
it.next() // {value: 'b', done: false}
it.next() // {value: 'undefined', done: false}

遍历器是一个对象,具有next方法,调用next方法,就可以遍历事先给定的数据结构

Iterator接口的目的,就是为所有的数据结构提供一种统一的访问机制,就是for of循环

默认的Iterator接口部署在数据结构的Symbol.iterator属性,或者说数据结构具有该属性就可以认为是可遍历的

有三类数据结构原生具备了Iterator接口:数组、类数组对象、Set和Map,可以直接使用for of方法
而对象需要在Symbol.iterator的属性上部署遍历器方法,才可以for of

下面是通过遍历器实现指针结构的例子

// 在原型链上部署System.iterator方法,调用该方法,会返回遍历器对象iteator。调用该对象的next方法,在返回一个值的同时,自动将内部指针指向下一个实例
function test (value) {
    this.value = value
    this.next = null
}

test.prototype[Symbol.iterator] = function () {
    let iterator = {
        next: next // iterator.next是一个function
    }

    let current = this
    
    function next () {
        if (current) {
            let value = current.value
            let done = current == null
            current = current.next
            return {
                value: value,
                done: done
            }
        } else {
            return {
                done: true
            }
        }
    }   
    return iterator
}

let one = new test(1),
    two = new test(2),
    three = new test(3);

one.next = two
two.next = three

for (let i of one) {
    console.log(i) // 1 2 3
}

还有一种简单操作,对于类数组对象

NodeList.prototypoe[Symbol.iterator] = Array.prototype[Symbol.iterator]

默认调用iterator接口的场合

  • 解构赋值
  • 扩展运算符
  • yield*
  • Arrat.from()
  • Map和Set

部署Symbol.iterator最简单实现是结合Generator

let myIterable = []
myiIterable[Symbol.iterator] = function* () {
    yield 1
    yield 2
    yield 3
}
[...myIterable] // [1, 2, 3]

// 或者
let obj = {
    * [Symbol.iterator]() {
        yield 'hello'
        yield 'world'
    }
}
for (let x of obj) {
    console.log(x) // hello world
}
  • 而对于对象来说,是不能直接使用for of的,需要使用yield包装以下
// 以下情况报错
let e = {
    a: 1,
    b: 'bbb'
}

for (let [key, value] of e) {
    console.log() // error,e is not iterable
}

// 使用yield包装一下
function* (obj) {
    for (let key of Object.keys(obj)) {
        yield [key, obj[key]]
    }
}

for (let [key, value] of entries(obj)) {
    console.log(key, '->', value) 
}
// a -> 1
// b -> 2
// c -> 3

JavaScript 原有的 for...in 循环,只能获得对象的键名,不能直接获取键值。ES6 提供 for...of 循环,允许遍历获得键值。

var arr = ["a", "b", "c", "d"];

for (a in arr) {
  console.log(a); // 0 1 2 3
}

for (a of arr) {
  console.log(a); // a b c d
}
阅读 2k

推荐阅读
JS学习笔记
用户专栏

夏姬八雪,杂七杂八

33 人关注
33 篇文章
专栏主页