乐趣区

弄懂ES6中的Iterator迭代器

本文云纹链接?

专门说一说 Iterator 迭代器的原因是,为后面 async/await 的文章做铺垫,因为我 async/await 是由 Generator+Promise 共同构成,而其中的 Generator 就是依赖于迭代器Iterator

下面让我们来看看什么是Iterator?它出现的目的是什么?如何使用它?

Iterator迭代器

名词:迭代 iteration / 可迭代 iterable / 迭代器 iterator

想必大家使用过 for 循环、while 循环等,遍历 Array 获取其中的值,那其他数据结构如何通过遍历获取呢?或者这样说,是否可以提供一个统一的访问机制?来访问 Object、Map、Set 等。

轮到 Iterator 迭代器出场,Iterator迭代器就是为了解决这个问题,它提供统一的接口,为 不同的数据结构提供统一的访问机制。(目前 Map、Set、Array 支持Iterator)。

顾名思义,Iterator迭代器的出现就是为了迭代而生,为不同的集合:Object、Array、Map、Set,提供了一个统一的接口(这里接口可以简单的理解为方法,就是遍历方法)。向我们常用的 for...of 就是依赖与 Iterator 迭代器。

在这里顺便提一嘴,我理解到的遍历、迭代的关系:遍历就是访问数据结构的所有元素,而迭代是遍历的一种形式。

// 阮一峰 ECMAScript 6 入门
// 模拟 next 方法返回值
var 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) {
  var nextIndex = 0;
  return {next: function() {
      return nextIndex < array.length ?
        {value: array[nextIndex++], done: false} :
        {value: undefined, done: true}
    }
  }
}

上面的 makeIterator 函数,它就是一个 迭代器生成函数 ,作用就是返回一个 迭代器对象 。对数组执行这个函数,就会返回该数组的 迭代器对象 it

通过调用 next 函数,返回 valuedone两个属性;value 属性返回当前位置的成员,done 属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用 next 方法;当 done 为 true 时,即遍历完成。

小结 Iterator 迭代器就是一个接口方法,它为不同的数据结构提供了一个统一的访问机制;使得数据结构的成员能够按某种次序排列,并逐个被访问

Iterator规范

在上面的代码中,迭代器对象 it 包含一个 next() 方法,调用next() 方法,返回两个属性:布尔值 done 和值value,value 的类型无限制。

迭代器对象包含的属性我们知道了,那么在日常开发中,我们如何让一个对象成为一个 可迭代对象 呢?(可迭代对象即支持迭代器规范的对象

要成为可迭代对象,一个对象必须实现 @@iterator 方法。这意味着对象(或者它原型链上的某个对象)必须有一个键为 @@iterator 的属性,可通过常量 Symbol.iterator 访问该属性。

let myIterable = {
    a: 1,
    b: 2,
    c: 3
}
myIterable[Symbol.iterator] = function() {
  let self = this;
  let arr = Object.keys(self);
  let index = 0;
  return {next() {return index < arr.length ? {value: self[arr[index++]], done: false} : {value: undefined, done: true};
    }
  }
}

var it = myIterable[Symbol.iterator]();

it.next();

for(const i of myIterable) {console.log(i);
}

myIterable 对象添加 Symbol.iterator 属性,同时在返回的 next 方法中,添加两个属性,既让它成为了一个可迭代对象。(其实如果真的有这样的需求,可以考虑使用Map)。

小结 Iterator 规范————Iterator迭代器包含一个 next() 方法,方法调用返回返回两个属性:donevalue;通过定义一个对象的Symbol.iterator 属性,即可将此对象修改为 迭代器对象 ,支持for...of 遍历。

IteratorGenerator

GeneratorPromise 一样,都是提供异步编程解决方案。其实 Generator 函数就是一个普通函数,但是有两个特征,一是,function 关键字与函数名之间有一个星号 *;二是,函数内部使用 yield 表达式,定义不同的内部状态。来看看 Generator 函数的使用:

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();
hw.next()
// {value: 'hello', done: false}
hw.next()
// {value: 'world', done: false}
hw.next()
// {value: 'ending', done: true}
hw.next()
// {value: undefined, done: true}

通过上面的例子可以知道,Generator函数执行后,会返回一个 Iterator 对象。在 Generator 中的 yield 表达式,yield 会记住当前代码运行的状态和位置,等在调用这串代码的时候会依次往后走。

Iterator(迭代器)就是一个可迭代的对象,而 Generator(生成器)使用了 yield 或者生成器表达式,生成 iterator 对象,用一种方便的方法实现了 iterator,在 for 循环取数据或使用 next() 取数据.

小结Generator(生成器)可以理解为是对Iterator(迭代器)的一种实现。

Iterator应用

Generator(生成器)就是其中最典型的一个应用,当然还有其他,例如:Map、Set、Array 等原生具备 Iterator(迭代器),支持for...of 循环。

Obejct 实现 Iterator 接口

Object 对象虽然不支持Iterator(迭代器),但我们可以使用Generator(生成器)进行包装。

let obj = {a: 1, b: 2, c: 3}
function* entries(obj) {for (let key of Object.keys(obj)) {yield [key, obj[key]];
  }
}
for (let [key, value] of entries(obj)) {console.log(key, '->', value);
}

目前在 Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象,原生具备Iterator 接口。

参考:

阮一峰:Iterator 和 for…of 循环

MDN 迭代协议

白话解释 迭代器 (ITERATOR) 和生成器(GENERATOR)

Python/Iterator and Generator 关系

退出移动版