提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部结构,迭代即遍历,作为一种相对简单的模式,绝大多数语言都内置了迭代器。
- 内部迭代器
最常见的如数组的forEach方法,你可以通过该方法遍历数组内的每一个元素:
[1, 2, 3].forEach((num) => {
console.log(num);
});
我们自己动手实现一下,该迭代器方法满足下面两个要求:
- 接收两个参数,一个数组,一个回调函数;
- 调用该迭代器,需提供有效的回调函数,参数为数组内的每一项;
function each(array, callback) {
for (let i = 0; i < array.length; i++) {
callback(array[i]);
}
}
each([1, 2, 3], (num) => {
console.log(num);
});
至此,一个简单的迭代器模型已经完成,那么在此基础上,我们试着改造一下内部迭代器的逻辑:
- 中止迭代器
function each(array, callback) {
for (let i = 0; i < array.length; i++) {
if (callback(array[i]) === false) {
break;
}
}
}
each([1, 2, 3], (num) => {
if (num > 1) {
return false;
}
console.log(num);
});
我们在迭代器内部,对于callback的返回值加了一层判断,如果返回false,那么跳过本地循环,这种模式,又称为中止迭代器。
- 外部迭代器
上面这两种迭代器,迭代规则都是被提前规定,外部调用不能关心迭代器内部实现,还有一种外部迭代器,它具有以下特点:
- 必须显示地请求迭代下一个元素;
- 增加了一些调用的复杂度,可以手工控制迭代的过程或者顺序;
function outIterator(obj) {
...
return {
next,
isDone,
getCurentItem,
length,
};
}
首先抛开内部实现,它返回以下内容:
- 迭代器的手动步进next方法;
- 迭代器是否结束的查询isDone方法;
- 获取当前迭代元素;
- 当前迭代对象的长度
基于上述四点需求,我们简单实现一个外部迭代器:
function outIterator(obj) {
const length = obj.length;
let curIdx = 0;
const next = () => {
curIdx += 1;
};
const isDone = () => {
return curIdx >= obj.length - 1;
};
const getCurentItem = () => {
return obj[curIdx];
};
return {
next,
isDone,
getCurentItem,
length,
};
}
let list = [1, 2, 3];
const outCtx = outIterator(list);
console.log(outCtx.getCurentItem()); // 1
outCtx.next();
outCtx.next();
console.log(outCtx.getCurentItem()); // 3
console.log(outCtx.isDone()); // true
虽然大部分能用到迭代器的地方,语言层面上内置的迭代器方法已经满足开发需要,但是通过了解迭代器模式,会为了让我们对于迭代的原理和实现有更清晰的认识和理解。