迭代器模式
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象 的内部表示。
迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不知道对象的内部构造,也可以按顺序访问其中的每个元素。
内部迭代器
可以类比成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); })
|
外部迭代器
外部迭代器必须显式地请求迭代下一个元素。外部迭代器增加了一些调用的复杂度,但相对也增强了迭代器的灵活性,我们可以手工控制迭代的过程或者顺序。(在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"); } catch (e) { if (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"); } catch (e) { return false; } }; const getFlashUploadObj = function () { if (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);
|