迭代
什么是迭代呢?
通俗的说法是,“按顺序反复多次执行一段程序”。
举一些🌰:
- 计数循环
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版)》