JavaScript迭代器模式
- 1 什么是迭代器模式
- 2 实现一个迭代器
- 3 内部迭代器和外部迭代器
- 3.1 内部迭代器
- 3.2 外部迭代器
- 4 迭代类数组对象和字面量对象
1 什么是迭代器模式
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。
JavaScript
有内置的迭代器Array.prototype.forEach
。
2 实现一个迭代器
下面来实现一个迭代器函数each
,each
函数接受2个参数,第一个为一个数组,第二个为每一次循环后要触发的回调函数,代码如下:
function each(arr, callback) {// 对arr循环遍历,每一次遍历调用callbackfor (let i = 0, l = arr.length; i < l; i++) {callback.call(arr[i], i, arr[i]);}
}
3 内部迭代器和外部迭代器
3.1 内部迭代器
刚才编写的each
函数属于内部迭代器,因为在each
函数内部已经定义好了迭代的规则,外部只需要调用一次就可以。
内部迭代器不关心具体的实现,因此调用时非常方便,但是这也刚好是内部迭代器的缺点,因为内部迭代器的迭代规则已经被提前规定好了,如果我们想同时迭代2个数组,上面的each
函数是无法实现的。
比如,判断2个数组里元素的值是否完全相等,不能改动each
函数,代码如下:
function compare(arr1, arr2) {// 如果两个数组长度不相同,不可能相等if (arr1.length !== arr2.length) {console.log("arr1和arr2不相等");return;}each(arr1, function (i, n) {// i为arr1每一项索引,n为arr1每项的值if (n !== arr2[i]) {console.log("arr1和arr2不相等");return;}console.log("arr1和arr2相等");});
}
3.2 外部迭代器
外部迭代器必须显式地请求迭代下一个元素,它增加了一些调用的复杂度,但相对也增强了迭代器的灵活性,我们可以手工控制迭代的过程或者顺序。
实现一个外部迭代器:
function Iterator(obj) {var current = 0; // 记录当前的索引// 下一个位置的索引var next = function () {current += 1;};// 是否已经迭代完成var isDone = function () {return current >= obj.length;};// 获取当前位置的数据var getCurrentItem = function () {return obj[current];};return {next,isDone,getCurrentItem,};
}
根据以上外部迭代器,我们可以这样改写compare
函数:
function compare(iterator1, iterator2) {while (!iterator1.isDone() && !iterator2.isDone()) {if (iterator1.getCurrentItem() !== iterator2.getCurrentItem()) {console.log("arr1和arr2不相等");return;}iterator1.next();iterator2.next();}console.log("arr1和arr2相等");
}var iterator1 = Iterator([1, 2, 3]);
var iterator2 = Iterator([2, 3, 4]);compare(iterator1, iterator2); // arr1和arr2不相等
外部迭代器虽然调用方式相对复杂,但它的适用面更广,也能满足更多变的需求。
4 迭代类数组对象和字面量对象
迭代器模式不仅可以迭代数组,还可以迭代一些类数组的对象,比如arguments
、{ 0: "a", 1: "b" }
等。无论是内部迭代器还是外部迭代器,只要被迭代的聚合对象拥有length
属性而且可以用下标访问,那它就可以被迭代。
在JavaScript
中,for in
语句可以用来迭代普通字面量对象的属性。jQuery
中提供了$.each
函数来封装各种迭代行为:
$.each = function (obj, callback) {var value,i = 0,length = obj.length,isArray = isArraylike(obj);if (isArray) {// 迭代类数组for (; i < length; i++) {value = callback.call(obj[i], i, obj[i]);if (value === false) {break;}}} else {for (i in obj) {// 迭代 object 对象value = callback.call(obj[i], i, obj[i]);if (value === false) {break;}}}return obj;
};