优雅的遍历:迭代器

阅读 1.7k
标签: JavaScript

在ECMAScript6之前,常用for循环来遍历数组,但是这种方式需要定义一个索引来记录每一次迭代的位置。这在多层for循环遍历时,会增加遍历的复杂度,且容易出错。为了更方便地进行迭代,ECMAScript6引入了迭代器(Iterator)。

什么是迭代器

迭代器并非JavaScript独创,在PythonJava等语言中,都有迭代器。那么在JavaScript中,迭代器到底长什么样呢?

在ECMAScript 6中,已经默认为一些类型提供了内置迭代器。对于集合对象来说,数组MapSet这三种类型都内建了以下三种迭代器:

  • entries() : 返回一个迭代器,其值为多个键值对
  • values():返回一个迭代器,其值为集合的值
  • keys():返回一个迭代器,其值为集合中的所有键名
来看一个小例子:
const arr = [100, 'abc', 200]
const it = arr.values()
console.log(it)
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())

arr.values()会返回一个迭代器,长这个样子:


它有一个next()方法,每次调用都会迭代一次数据,获得的结果是一个对象(包含一个value属性和一个done属性),当所有数据都迭代完成后,最终返回的对象为{value: undefined, done: true}

上面代码的迭代结果如下:


黄金搭档:for-of

看起来感觉有点麻烦,不是吗?为了迭代一次数据,还得每次调用next()?所幸的是,我们有迭代器的黄金搭档:for-of循环,这才是真正提高生产力的工具。
当迭代器搭配for-of循环,遍历就变成了一个简单清晰的任务,我们来看一个小例子:
const arr = [100, 'abc', 200]
const it = arr.values()

for (let value of it) {
  console.log(value)
}

请注意:for-of循环是用在迭代器上的,不是用在数组上的。但是,如果直接用在数组上,似乎也没问题:

const arr = [100, 'abc', 200]

for (let value of arr) {
  console.log(value)
}

二者结果完全一样,数组本身并不是迭代器,那为啥可以使用for-of呢?

这是因为JavaScript引擎获得了迭代器(会自动调用arr数组原型对象上的Symbol.iterator方法,结果是返回一个迭代器)。

如果将for-of语句用于不可迭代的对象,会导致程序抛出异常:

const obj = {
  name: 'foo',
  age: 20
}

for (let value of obj) {
  console.log(value)
}

obj对象并不是一个可迭代的对象,所以执行时出现了错误:Uncaught TypeError: obj is not iterable

实际应用

最后,说说在实际开发过程中,常用的一些迭代器技巧。

对于数组、Set和Map,可以直接使用for-of来进行迭代。除此之外,字符串NodeList也可以使用for-of来迭代:

const msg = 'abc'
for (let c of msg) {
  console.log(c)
}

const divs = document.getElementsByTagName("div")
for (let div of divs) {
  console.log(div)
}

另外,展开运算符(...)可以操作所有可迭代对象。

展开运算符

来看一个例子:

const set = new Set([100, 200])
const arr = ['aa', 'bb']
const result = [...set, ...arr, 300]

展开运算符会将所有可迭代对象进行展开,所以,上面的结果会到了一个新数组[100, 200, 'aa', 'bb', 300]

小结

现在,相信大家已经对迭代器有了大概的理解了,但是有一个问题,上面所见的都是语言内置的迭代器对象,如果让要一个普通对象也可以迭代,需要怎么做呢?或者需要自定义迭代器,怎么办呢?请见下一篇《迭代器工厂:生成器》。

参考

  • 深入理解ES6,Nicholas C. Zakas
最后编辑于: 2022-06-28

评论(0条)

(必填)
复制成功