1 迭代器模式的定義
迭代器模式是指提供一種方法順序訪問
一個聚合物件
中的各個元素,而又不需要暴露該物件的內部表示。
迭代器模式可以把迭代的過程從業務邏輯中分離出來,在使用迭代器模式之後,即使不關心物件的內部構造,也可以按順序訪問其中的每個元素。
2 jquery中的迭代器
迭代器模式無非就是迴圈訪問局和物件中的每個元素。例如jquery中的$.earch函式。其中回撥函式中的引數i為當前索引,n為元素。
$.each([1, 2, 3], function(i, n) {
console.log(`當前下標:${i}`);
console.log(`當前值為:${n}`);
})
複製程式碼
3 實現自己的迭代器
我們來實現一個擴充陣列的eachPlus函式,實現each函式相同的遍歷功能。引數是一個回撥函式
Array.prototype.eachPlus = function(callback) {
var self = this;
for(var i = 0; i < self.length; i++) {
callback(self[i], i, self);
}
}
var arr = [1, 2, 3];
arr.eachPlus(function(value, index) {
console.log(value, index);
})
複製程式碼
4 內部迭代器與外部迭代器
- 內部迭代器:函式內部已經定義好迭代規則, 它完全接手整個迭代過程,外部只需要一次初始化呼叫。
- 外部迭代器:必須顯示請求迭代下一個元素。 由於內部迭代器已經將迭代過程定義好了,因此我們想要在規則中修改程式碼是不可能了。假如我們想要比較兩個陣列是否相等,只能在回撥函式中進行著手:
function compare(arr1, arr2) {
arr1.eachPlus(function(value, index) {
if (arr1.length !== arr2.length) {
throw new Error('arr1 != arr2');
}
if (arr2[index] !== value) {
throw new Error('arr1 != arr2');
}
console.log('arr1 == arr2');
});
}
var arr1 = [1, 2, 3];
var arr2 = [1, 2, 3];
compare(arr1, arr2);
複製程式碼
外部迭代器增加了一些呼叫的複雜度,但是相對增強了迭代器的靈活性,我們可以手工控制迭代的過程或者順序。下面是一個外部迭代器的例子
Array.prototype.iterator = function() {
var self = this;
var index = 0;
// 獲取當前元素
var current = function() {
return self[index];
};
// 獲取當前元素,並移動索引到下一個位置
var next = function() {
var el ;
if (!hasNext()) {
return null;
}
el = current();
index++;
return el;
};
// 是否還有元素
var hasNext = function() {
return index < self.length ? true : false;
};
// 重置迭代器
var rewind = function() {
index = 0;
};
return {
next: next,
hasNext: hasNext,
current: current,
rewind: rewind
}
}
// 呼叫
function compare(arr1, arr2) {
var iter1 = arr1.iterator();
var iter2 = arr2.iterator();
while (iter1.hasNext() || iter2.hasNext()) {
if (iter1.current() !== iter2.current()) {
throw new Error('不相等');
}
iter1.next();
iter2.next();
}
console.log('相等');
}
compare([1,2,3,4], [1,2,3,4])
複製程式碼
5 迭代器模式應用
當上傳一個檔案,我們有一個上傳的控制元件。但是不同的瀏覽器相容問題,應該使用不一樣的控制元件。
- 獲取IE控制元件
- 不存在則使用flash控制元件
- 以上都不存在使用原生表單上傳
var getUploadObj = function(){
try{
return new ActiveXObject("TXFTNActiveX.FTNUpload"); // IE 上傳控制元件
} catch(e) {
if (supportFlash()){ // supportFlash 函式未提供
var str = '<object type="application/x-shockwave-flash"></object>';
return $( str ).appendTo( $('body') );
} else {
var str = '<input name="file" type="file"/>'; // 表單上傳
return $( str ).appendTo( $('body') );
}
}
};
複製程式碼
上面的程式碼中,為了得到一個upload控制元件,使用了很多if,else,並且使用了try catch。這個例子就像是我們有1扇門,手裡有三把鑰匙,不知道拿一把可以開啟,那麼就只有一直嘗試,直到正確為止。下面我們嘗試使用內部迭代器模式
解決這個問題:
- 將上傳控制元件各自封裝
- 組裝空間陣列
- 進行迭代獲取正確鑰匙
var IEUpload = function() {
try{
return new ActiveXObject("TXFTNActiveX.FTNUpload"); // IE 上傳控制元件
} catch(e) {
return false;
}
}
var flashUpload = function() {
if (supportFlash()) { // supportFlash 函式未提供
var str = '<object type="application/x-shockwave-flash"></object>';
return $( str ).appendTo( $('body') );
}
return false;
}
var getFormUpload = function() {
var str = '<input name="file" type="file"/>'; // 表單上傳
return $( str ).appendTo( $('body') );
}
// 編寫內部迭代器模式
function getUpload() {
for(var i = 0, fn; fn = arguments[i++];) {
var uploadObj = fn();
if (uploadObj) {
return uploadObj;
}
}
}
// 使用內部迭代器
getUpload(IEUpload, flashUpload, getFormUpload);
複製程式碼
前面提到:將不變的部分
和變化的部分
隔開是每個設計模式的主題。這裡我們也可以看出來,getUpload就是分離出來的不可變的部分
。當我們需要增加一個HTML5的上傳控制元件型別時,不再需要修改整個程式碼,只需要新增一個上傳規則html5Upload()函式
在進行呼叫即可。
6 小結
迭代器模式比較簡單,很多時候許多人都認為它不是一種設計模式。目前絕大部分語言都內建了迭代器。