Iterator 接口
Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of
循环。当使用for...of
循环遍历某种数据结构时,该循环会自动去寻找并调用Symbol.iterator
方法。也就是说,一种数据结构只要部署了Symbol.iterator
属性,就被视为具有iterator
接口,我们就称这种数据结构是“可遍历的”(iterable
)。
Symbol.iterator
属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回默认的遍历器。至于属性名Symbol.iterator
,它是一个表达式,返回Symbol
对象的iterator
属性,这是一个预定义好的、类型为Symbol
的特殊值,所以要放在方括号内。
下面是原生具备Iterator
接口的数据结构:
- Array
- Map
- Set
- String
- TypedArray
- 类数组的对象(比如 arguments 对象、DOM NodeList 对象)
- Generator 对象
只要原型链上的对象具有该方法,就可以执行:
- 解构赋值
- 扩展运算符
- yield*
- for…of
- Array.from()
- Map(), Set()
- Promise.all()
- Promise.race()
遍历数组
数组原生具备iterator
接口。
1 | const arr = ['a', 'b', 'c', 'd'] |
遍历类数组
1 | // 字符串 |
并不是所有类似数组的对象都具有iterator
接口,一个简便的解决方法,就是使用Array.from
方法将其转为数组。
1 | const arrayLike = { length: 2, 0: 'a', 1: 'b' } |
遍历对象
对于普通的对象,for...of
结构不能直接使用,会报错,必须部署了iterator
接口后才能使用。那么怎么做到使对象可遍历?
方法一:部署 Iterator 接口(在 Symbol.iterator 的属性上部署遍历器生成方法)
一个对象如果要具备可被for...of
循环调用的iterator
接口,就必须在Symbol.iterator
的属性上部署遍历器生成方法。
1、添加 next 方法:
1 | const obj = { |
1 | const arr = ['red', 'green', 'blue'] |
原型链上的对象具有该方法也可以。
1 | class RangeIterator { |
2、Generator:
1 | class Collection { |
由于Generator
函数就是遍历器生成函数,因此可以把Generator
赋值给对象的Symbol.iterator
属性,从而使得该对象具有iterator
接口。
1 | var myIterable = {} |
方法二:使用 Object.keys()、Object.values()、Object.entries()
1 | const obj = { a: 1, b: 2, c: 3 } |
方法三:使用 Object.getOwnPropertyNames()
包含不可枚举属性,不包含Symbol
属性。
1 | for (const key of Object.getOwnPropertyNames(obj)) { |
方法四:使用 Reflect.ownKeys()
包含不可枚举属性,也包含Symbol
属性。基本等同于Object.getOwnPropertyNames
与Object.getOwnPropertySymbols
之和。
1 | for (const key of Reflect.ownKeys(obj)) { |
方法五:使用 Generator 函数将对象重新包装
1 | function* entries(obj) { |
方法六:使用 Map 代替对象
1 | const map = new Map(Object.entries(obj)) |