JavaScript系列——迭代器

30 阅读2分钟

迭代

什么是迭代呢?

通俗的说法是,“按顺序反复多次执行一段程序”。

举一些🌰:

  • 计数循环
for(let i=0; i<=9; i++){
 console.log(i)   
}
  • 数组循环
[1,2,3,4,5].forEach(v => console.log(v))

上述方法实现迭代的缺点

  • 计数循环

比如我们有一个数组

const fruit = ['apple', 'orange', 'pear'];
for (let index = 0; index < fruit.length; ++index) {
    console.log(fruit[index]);
}

我们在使用计数循环的时候,我们首先得要确定该数据结构是怎么样子的,在这里我们使用了[]去访问数组对应索引的值,但是有些数据结构并不能这么使用。迭代之前需要事先知道如何使用数据结构。

另外就是,遍历顺序并不是数据结构固有的。 通过递增索引来访问数据是特定于数组类型的方式,并不适用于其他具有隐式顺序的数据结构。

  • 数组循环

使用forEach的时候我们通常不知道什么时候终止它,当然也可以用try catch抛出错误的方式去强行终止循环,但是不建议这么使用。

许多语言通过原生语言去解决这些问题,开发者们无需知道如何迭代就能实现迭代操作,这个解决方案就是 “迭代器模式”, JavaScript 在 ECMAScript 6 以后也支持了迭代器模式。

迭代器模式

迭代器模式:把有些结构称为“可迭代对象”(iterable),因为它们实现了Iterable接口,而且可以通过Iterator消费。

...这说的是个啥啊

不急让我们慢慢理清楚 (〃'▽'〃)

迭代器:一个按需创建的对象,可迭代对象会暴露出一个Symbol.iterator属性,该属性的值是一个“迭代器工厂函数”,调用该工厂函数会返回一个默认的迭代器。

迭代器怎么迭代:迭代器上会使用next() 方法在可迭代对象中迭代数据,该方法返回一个迭代器对象,包含两个属性:done(是否结束了) 和 value(当前的值)

举个🌰:

//一个可迭代对象
const arr = [1, 2, 3] 

//迭代器工厂函数
console.log( arr[Symbol.iterator] ) //ƒ values() { [native code] }

//调用函数返回迭代器
let iter =  arr[Symbol.iterator]()   // Array Iterator {}

//使用迭代器上的next方法
iter.next()  // {value: 1, done: false}
iter.next()  // {value: 2, done: false}
iter.next()  // {value: 3, done: false}
iter.next()  // {value: undefined, done: true}  done为true表示结束了,返回value为undefined

哪些类型实现了Iterable接口呢(Symbol.iterator属性)

  • 字符串
  • 数组
  • 集合
  • 映射
  • arguments对象
  • NodeList

这些类型难道我每次迭代都要先拿到迭代器,然后Next,再...

当然不需要啦!(^ω^)

在实际写代码的过程中,我们并不需要显式调用工厂函数去生成迭代器,JavaScript语言已经用一些语言结构帮我们处理好了中间的过程,例如:

  • for-of循环
  • 数组结构
  • 扩展操作符
  • Array.from()
  • 创建集合
  • 创建映射
  • yield * 操作符在生成器中使用
  • ...

上述的这些语言结构会在后台去调用可迭代对象的工厂函数,创建迭代器。

const fruit = ['apple', 'fruit', 'pear']

// for of
for (let item of fruit) {
    console.log(item)
}

// 解构
const [a, b, c] = fruit

// 扩展运算符
const [...d] = fruit

怎么去提前终止一个迭代呢

最常见的 for-of循环可以使用 break 、 continue 、 return 或 throw 提前退出。

部分迭代器实例会带有return方法去终止迭代,return() 方法必须返回一个有效的 IteratorResult 对象。简单情况下,可以只返回 { done: true } 。

举个🌰:

const arr = [1,2,3,4,5]
for(let num of arr) {
    if(num < 3) {
        console.log(num)
    } else {
        break
    }
}

// 1 2

注意的是, return()是可选的,并非所有迭代器都是可以关闭的,我们可以通过观察迭代器的return属性是不是函数对象来判断,同时我们可以手动添加return方法,让迭代器提前终止:

const arr = [1, 2, 3, 4, 5]
const iter = arr[Symbol.iterator]()
iter.return = function() {
    console.log('Exiting early');
    return { done: true };
}

for (let i of iter) {
    console.log(i);
    if (i > 2) {
        break
    }
}
// 1
// 2
// 3
// 提前退出

for (let i of iter) {
    console.log(i);
}
// 4
// 5

参考《JavaScript高级程序设计(第4版)》