一、什么是迭代器
迭代器是一种设计模式,这种模式是为了解决集合等容器的遍历问题,使用者无需关注容器内部的实现。从实现上看,迭代器每一次得到的结果都会作为下一次迭代的值,这个通常使用指针维护,而不是单纯的重复。下面是js实现迭代器接口示例
const myContainer = {
//数组作为容器,拥有length属性。
store: ["apple", "banner", "pearl"],
[Symbol.iterator]: function () {
let index = 0;
const self = this;
return {
next: function () {
const result = {
value: self.store[index],
done: index >= self.store.length,
};
index++;
return result;
},
};
},
};
for (const item of myContainer) {
console.log("loop:" + item);
}
二、ES6应用
ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费. 所有拥有Symbol.iterator属性的数据结构(值),都是可被迭代的,并且可以基于for of循环遍历。 常见的应用如下
1、结构后赋值
Set和Array的遍历基于迭代器应用示例如下:
const [a, b] = [1, 2];
//a: 1, b: 2
const [c, d] = new Set([3, 4]);
//c: 3, d: 4
Javascript引擎分别在左右两侧结构上调用next方法,逐个赋值,左侧数组的每个变量会对应被赋为右侧的值。
2、扩展运算符
ES6的扩展运算符也是通过Iterator接口实现,示例如下:
let args = ['username', 'gender', 'age'];
foo(...args);
//等价于foo('username', 'gender', 'age')
[Symbol.iterator]的对象都可以用扩展运算符展开,如开头的自定义示例
3、异常的处理
let obj = {
username: '张三',
gender: 1,
[Symbol.iterator]: function (){
let index = 0;
let keys = ['username', 'gender'];
let _this = this;
return {
next(){
return index < keys.length ?
{value: _this[keys[index++]], done: false} :
{value: undefined, done: true}
},
return(){
... //结束循环前可以在这里执行某些操作,如关闭文件系统等
return {done: true}
},
throw(){
... //抛出异常时可以在这里执行某些操作
return {done: true}
}
}
}
}
for(let val of s){
console.log(val);
break; //该语句会触发遍历器对象的return方法
}
for(let val of s){
console.log(val);
throw new Error(); //该语句会触发遍历器对象的throw方法
}
三、迭代器优点和缺点
优点
- 为集合和非集合类型提供了统一的取值策略,无需修改原有代码。
- 调用的时候非常的方便,外界不用关心迭代器内部的实现,跟迭代器的交互也仅仅是一次初始化调用
缺点
- 除非遍历到最后,否则无法获取迭代器长度
- 迭代只能取到下一个值,不能回到开始位置,同一个迭代器被多个循环使用,只有一个循环能够获取到值
- 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
四、总结
迭代器是ES6最为重要和基础的概念之一,很多新的ES6的语法都在它的基础上实现。 他是一种线性输出“集合”属性值的机制,方便了扩展和使用。另外迭代器的更多能力应该和生成器一起使用才能够体现出来,我们将在下一篇探讨迭代器与生成器的配合。