本文云纹链接
专门说一说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
函数,返回value
和done
两个属性;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()
方法,方法调用返回返回两个属性:done
和value
;通过定义一个对象的Symbol.iterator
属性,即可将此对象修改为迭代器对象
,支持for...of
遍历。
#Iterator
和Generator
Generator
和Promise
一样,都是提供异步编程解决方案。其实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 关系