JavaScript设计模式与开发实践(四)迭代器模式

迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象 的内部表示。

迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不知道对象的内部构造,也可以按顺序访问其中的每个元素。

内部迭代器

可以类比成JavaScript数组中的forEach方法。函数的内部已经定义好了迭代规则,它完全接手整个迭代过程,外部只需要一次初始调用。

1
2
3
4
5
6
7
8
9
10
function forEach(arr, callback) {
for (let i = 0; i < arr.length; i++) {
if (callback) {
callback(i, arr[i])
}
}
}
forEach([1, 2, 3], (index, item) => {
console.log(index, item);
})

同时我们希望当传入的函数返回false终止迭代。

1
2
3
4
5
6
7
8
9
10
11
12
13
function forEach(arr, callback) {
for (let i = 0; i < arr.length; i++) {
if (callback(i, arr[i]) === false) {
break
}
}
}
forEach([1, 2, 3], (index, item) => {
if (item > 2) {
return false
}
console.log(item); // 1 2
})

外部迭代器

外部迭代器必须显式地请求迭代下一个元素。外部迭代器增加了一些调用的复杂度,但相对也增强了迭代器的灵活性,我们可以手工控制迭代的过程或者顺序。(在JavaScript中的实现是Iterator)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Iterator(obj) {
let current = 0
function next() {
return {
value: obj[current++],
done: current === obj.length
}
}
return {
next
}
}
const iterator = Iterator({ 0: 'a', 1: 'b', 2: 'c', length: 3 })
const res1 = iterator.next()
console.log(res1.done,res1.value);
const res2 = iterator.next()
console.log(res2.done,res2.value);
const res3 = iterator.next()
console.log(res3.done,res3.value);

应用

在不同的浏览器环境下,选择的上传方式是不一样的。因为使用浏览器的上传控件进行上传速度快,可以暂停和续传,所以我们首先会优先使用控件上传。如果浏览器没有安装上传控件, 则使用 Flash上传,如果Flash也没安装,那就使用浏览器原生的表单上传。

但是下面的实现方式有些问题,充斥着try catch 和一些if else语句,同时后续可能会有一些新的上传方式,这显然不是我们所期望的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const getUploadObj = function () {
try {
return new ActiveXObject("TXFTNActiveX.FTNUpload"); // IE 上传控件
} catch (e) {
if (supportFlash()) { // supportFlash 函数未提供
const str = '<object type="application/x-shockwave-flash"></object>';
return $(str).appendTo($('body'));
} else {
const str = '<input name="file" type="file"/>'; // 表单上传
return $(str).appendTo($('body'));
}
}
};
getUploadObj()

我们可以将不同方式的上传各自封装成函数,交给迭代器来进行迭代,选择一个可用的上传方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const getActiveUploadObj = function () {
try {
return new ActiveXObject("TXFTNActiveX.FTNUpload"); // IE 上传控件
} catch (e) {
return false;
}
};
const getFlashUploadObj = function () {
if (supportFlash()) { // supportFlash 函数未提供
const str = '<object type="application/x-shockwave-flash"></object>';
return $(str).appendTo($('body'));
}
return false;
};
const getFormUpladObj = function () {
const str = '<input name="file" type="file" class="ui-file"/>'; // 表单上传
return $(str).appendTo($('body'));
};
const iteratorUploadObj = function () {
for (let i = 0, fn; fn = arguments[i++];) {
const uploadObj = fn();
if (uploadObj !== false) {
return uploadObj;
}
}
};
const uploadObjList = [getActiveUploadObj, getFlashUploadObj, getFormUpladObj]
const uploadObj = iteratorUploadObj(...uploadObjList);

JavaScript设计模式与开发实践(四)迭代器模式
https://jing-jiu.github.io/jing-jiu/2023/01/07/notebooks/JavaScript设计模式与实践/设计模式(四)/
作者
Jing-Jiu
发布于
2023年1月7日
许可协议