概述
Iterator
接口允许为所有数据结构,提供了一种统一的遍历机制,被 for...of
使用这种机制遍历这些数据结构, 一些内置类型都是内置的可遍历对象并且有默认的迭代行为, 比如 Array
, 另一些类型则不是, 比如 Object
。
为了变成可遍历解构,对象或其原型链上某个对象必须实现 @@iterator
方法,即属性名为 Symbol.iterator
。
当该对象需要被遍历的时候(比如用于一个 for..of
循环中),它的 @@iterator
方法被调用并且无参数,然后返回一个用于在遍历中获得值的迭代器,具体过程如下:
- 创建一个指针对象,指向当前数据结构的起始位置;
- 第一次调用该指针对象的
next
方法,指向数据结构的第一个成员,返回结果是一个包含value
和done
两个属性的对象。 value
字段表示当前遍历的成员的值。- 如果
done
的值为true
,则遍历结束,否则,done
的值为false
,下次会继续遍历下一个成员。
可以看一下数组的默认遍历器
let arr = ["a", "b"]; |
- 第 1 次调用
iter.next()
,返回数组的第1个元素value
为a
,以及done
的值为fasle
,表示遍历没有结束,还可以继续遍历。 - 第 2 次调用
iter.next()
,返回数组的第2个元素value
为b
,以及done
的值还是为fasle
,表示遍历没有结束,还可以继续遍历。 - 第 3 次调用
iter.next()
,返回的value
为undefined
,done
的值为true
,遍历结束。
了解了这些,就可以创建一个可遍历的对象,并定义它的遍历行为:
let obj = { |
Generate
如果了解 Generate
,就会发现这个和 Generate
的结构非常相似,那么遍历函数可以使用 Generate 吗?
答案是可以,接下来使用 Generate
函数改写上面的例子:
let obj = { |
使用 Generate
更加节省代码,而且流程也更加清晰,更容易理解。
内置支持迭代的对象
String
、Array
、TypedArray
、Map
和 Set
都内置迭代器, 因为它们的原型对象都有一个 @@iterator
方法:
var string = "hello"; |
支持迭代操作的语句、表达式和方法
除了内置迭代器的对象,还有一些场合下也会使用迭代器:
解构赋值
对数组和Set结构进行执行解构操作的时候,会调用对象的迭代器:
let array = ["a", "b", "c"]; |
扩展运算符
扩展运算符(...
)也会调用默认的 iterator
接口:
let hello = "hello"; |
对象默认不支持迭代器,因此对象的解构赋值和扩展运算符并不是使用迭代器的:
var {a, ...others} = {a: 1, b: 2, c: 3} |
for..of 循环
前文已说明,不再多言
常见的类数组对象
常见的一些类数组对象,如 arguments
、 NodeList
等也支持迭代器:
(function test() { |
但并不是所有的类数组对象都支持迭代器,所以在使用的时候,可以先将其转换为数组:
var arrLike = { |
Generators
generator函数自身就是一个迭代器,当然也可以使用迭代器:
function * list() { |
接受数组或可迭代对象作为参数
任何接受数组作为参数的场合,其实都调用了遍历器接口,如 Array.from()
、Map()
, Set()
, WeakMap()
, WeakSet()
、Promise.all()
、Promise.race()
:
var arr = [1, 2, 3]; |
for 循环、for…in 以及 forEach等数组函数遍历对比
在有些应用场景下,这些遍历方式可以通用,这个根据实际的应用场景选择即可。
for 循环
for
语句遍历就一个特点,写起来麻烦,应用场景到时最普遍的。for
语句遍历对象的时候,如果遍历对象需要搭配其他函数,如 Object.keys(object)
,获得对象的键值 keys
的数组
for..in
for..in
可以遍历对象和数组对象,但有一些情况需要注意:
index
的索引为字符串的数字,不能直接进行数值的运算:
var arr = [1, 2, 3]; |
for...in
会遍历对象上的所有可枚举属性,包括原型方和原型属性:
var obj = { |
如果不想遍历原型方法和原型属性的话可以使用 hasOwnPropery
做一个判断:
for (var key in obj) { |
也可以使用 Object.keys(object)
获取对象自身的实例属性组成的数组:
var keys = Object.keys(obj); |
forEach 等数组函数
数组上也有一些方法支持遍历数组,比如 forEach
、map
、filter
、every
、reduce
等函数,这些函数都有着不同的用途,但是最主要的问题在于他们遍历数组的操作是在回调函数中执行、不能使用 continue
、break
中断遍历,而且不能直接 return
返回到外部。