ES6篇 - Iterator
Iterator
参考文章: ES6阮一峰 - Iterator 和 for…of 循环
Iterator 概念
Iterator (遍历器对象) 是为各种数据结构(Array, Object, Map, Set, …)提供的一个统一简便的访问接口;
Iterator 接口主要供 ES6 的遍历命令 for…of 消费, 任何部署了 Iterator 接口的数据结构都可以完成遍历操作(程序可依次处理该数据结构的所有成员);
Iterator 能够将数据结构的成员按照某种次序进行排序;
Iterator 为程序遍历各种数据结构提供了统一的方法, ES6 的 for…of 通过访问和消耗 Iterator 接口, 实现对数据结构内所有成员的遍历操作
遍历 Iterator 的过程:
- 创建一个指针对象, 指向当前数据结构的起始位置;
- 调用指针对象的 next 方法, 将指针指向数据结构的第一个成员, 返回成员信息; 每次调用 next 方法都会移动指针, 指向下一个成员, 并返回成员信息;
- 不断调用指针对象的 next 方法, 直到指向数据结构结束位置, 停止遍历;
注意:
Iterator 开始遍历时创建的指针对象, 其指向的起始位置不是数据结构的第一个成员;
Iterator 每一次调用 next 方法, 都会返回当前指向的数据结构成员的成员信息(一个包含 value 和 done 属性的对象: {value: any, done: boolean}
)
Iterator 结束位置不是数据结构的最后一个成员, Iterator 遍历结束的信号是当 done === true
时;
Code
要实现一个 Iterator 遍历器对象, 则主要实现三个要求:
- 返回一个对象
- 对象内包含 next 方法; next() {…} 是一个无参函数
- next 方法返回一个
{value: any, done: boolean}
对象
Iterator 实现用到了闭包的思想, 当前遍历的索引变量被保留在了内存中, 没有随着 makeIterator 函数执行结束后上下文销毁而消失;
1 |
|
Iterator 遍历操作实现:
1 |
|
可迭代对象
上述我们实现了手动调用 Iterator 遍历器对象的 next 方法, 模拟对数据结构所有成员的遍历过程, ES6 通过 for…of 遍历数据结构时, 会自动寻找 Iterator 接口, 我们称部署了 Iterator 接口的数据结构为”可遍历对象(Iterable)”
注: Iterator 遍历器对象是不可以被 for…of 遍历的, 因为它只是一个指针对象, 只有部署了它的数据结构才是 for…of 遍历的目标;
ES6 规定, 默认的 Iterator 接口要部署在数据结构的 Symbol.iterator
属性上; Symbol.iterator
属性是当前数据结构默认的遍历器生成函数; Symbol.iterator
是一个表达式, 返回 Symbol 对象的 iterator 属性, 作为属性名时用方括号引用;
for…of 只能遍历实现了 Symbol.iterator 属性部署的数据结构 (若目标原型链上具有 Symbol.iterator 属性也可被遍历); for…of 每次循环调用 next 方法后, 都会检查返回值 done 属性, 若遍历未结束 done === false, 则继续调用 next 方法循环, 若检查到 done === true, 则结束遍历, 且不会将 value 赋值给临时变量;
Code
实现一个可迭代对象(可被遍历的数据结构), 需满足以下几点:
- 数据结构内包含
Symbol.iterator
属性; Symbol.iterator
属性值是一个函数, 返回一个遍历器对象(上述已实现)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16const arrayLike = {
'0': 'Hello',
'1': 'World',
'length': 2,
[Symbol.iterator]() {
const ctx = this;
let idx = 0;
return {
next() {
return idx < ctx.length ?
{ value: ctx[idx++], done: false } :
{ value: undefined, done: true }
}
}
}
}
ES6 原生具备 Iterator 接口的数据结构:
- Array
- Map
- Set
- String
- TypedArray
- arguments
- NodeList
普通对象没有原生 Iterator 接口部署, 需要自己在 Symbol.iterator 属性上部署, 才能被 for…of 循环遍历; (对象 Object 之所以没有默认部署 Iterator 接口, 是因为对象属性遍历的顺序是不确定的, 需要开发者手动指定)
遍历 Iterator 接口的场景
除了 for…of 循环外, 还有几个操作也要求数据结构部署 Iterator 接口;
- 解构赋值
- 扩展运算符
yield*
- Array.from()
- Map(), Set(), WeakMap(), WeakSet()
- Promise.all()
- Promise.race()
for…of 循环
ES6 遍历所有数据结构的统一方法;
前提: 数据结构具有 Symbol.iterator
属性, 即具有 iterator 接口;
本质: for…of 循环内部调用数据结构的 Symbol.iterator
方法, 其返回一个迭代器对象, 并执行 next 方法, next 方法会返回 {value: any, done: boolean}
对象, 检查是否 done === true
, 若不是则将 value 值赋值给 for…of 临时变量, 若是则终止迭代, 且不执行赋值操作;
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!