javascript設計模式 之 4 迭代器模式

zhaoyezi發表於2018-05-30

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 小結

迭代器模式比較簡單,很多時候許多人都認為它不是一種設計模式。目前絕大部分語言都內建了迭代器。

相關文章