JavaScript設(shè)計(jì)模式之迭代器模式(Iterator)

迭代器模式是一種相對(duì)簡(jiǎn)單的模式,簡(jiǎn)單到很多時(shí)候我們都不認(rèn)為它是一種設(shè)計(jì)模式。目前的絕大部分語(yǔ)言都內(nèi)置了迭代器。

比如:JavaScript 的 Array.prototype.forEach

jQuery里一個(gè)非常有名的迭代器就是 $.each 方法,通過each我們可以傳入額外的function,然后來對(duì)所有的item項(xiàng)進(jìn)行迭代操作,例如:

$.each( [1, 2, 3], function( i, n ){
    console.log( '當(dāng)前下標(biāo)為: '+ i,'當(dāng)前值為:' + n );
});

// 下標(biāo): 0 當(dāng)前值:1 
// 下標(biāo): 1 當(dāng)前值:2  
// 下標(biāo): 2 當(dāng)前值:3

定義

迭代器模式(Iterator):提供一種方法順序一個(gè)聚合對(duì)象中各個(gè)元素,而又不暴露該對(duì)象內(nèi)部表示。

迭代器的幾個(gè)特點(diǎn)是:

  • 訪問一個(gè)聚合對(duì)象的內(nèi)容而無需暴露它的內(nèi)部表示。
  • 為遍歷不同的集合結(jié)構(gòu)提供一個(gè)統(tǒng)一的接口,從而支持同樣的算法在不同的集合結(jié)構(gòu)上進(jìn)行操作。
  • 遍歷的同時(shí)更改迭代器所在的集合結(jié)構(gòu)可能會(huì)導(dǎo)致問題(比如C#的foreach里不允許修改item)。

使用實(shí)例

實(shí)現(xiàn)自己的迭代器

參照jQuery的$.each 方法, 我們來自己實(shí)現(xiàn)一個(gè) each 函數(shù),each 函數(shù)接受 2 個(gè)參數(shù),第一個(gè)為被循環(huán)的數(shù)組,第二個(gè)為循環(huán)中的每一步后將被觸發(fā)的回調(diào)函數(shù):

var each = function( ary, callback ) {
    for ( var i = 0, l = ary.length; i < l; i++ ){
        callback.call( ary[i], i, ary[ i ] ); 
        // 把下標(biāo)和元素當(dāng)作參數(shù)傳給callback 函數(shù)
    }
};

each( [ "a", "b", "c" ], function( i, n ){
    console.log( '自定義下標(biāo)為: '+ i,'自定義值為:' + n );
});

// 自定義下標(biāo)為: 0 自定義值為:a  
// 自定義下標(biāo)為: 1 自定義值為:b 
// 自定義下標(biāo)為: 2 自定義值為:c

迭代器模式的應(yīng)用舉例

以常用的上傳文件功能為例,在不同的瀏覽器環(huán)境下,選擇的上傳方式是不一樣的。因?yàn)槭褂脼g覽器的上傳控件進(jìn)行上傳速度快,可以暫停和續(xù)傳,所以我們首先會(huì)優(yōu)先使用控件上傳。如果瀏覽器沒有安裝上傳控件, 則使用 Flash 上傳, 如果連 Flash 也沒安裝,那就只好使用瀏覽器原生的表單上傳了,代碼為:

var getUploadObj = function() {
    try {
        return new ActiveXObject("TXFTNActiveX.FTNUpload"); // IE 上傳控件
    } catch (e) {
        if (supportFlash()) { // supportFlash 函數(shù)未提供
            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'));
        }
    }
};

看看上面的代碼,為了得到一個(gè) upload 對(duì)象,這個(gè) getUploadObj 函數(shù)里面充斥了 try,catch 以及 if 條件分支。缺點(diǎn)是顯而易見的。

現(xiàn)在來梳理一下問題,目前一共有 3 種可能的上傳方式,我們不知道目前瀏覽器支持那種上傳方式,那就需要逐個(gè)嘗試,直到成功為止,分別定義以下幾個(gè)函數(shù):

// IE 上傳控件
var getActiveUploadObj = function() {
    try {
        return new ActiveXObject("TXFTNActiveX.FTNUpload"); 
    } catch (e) {
        return false;
    }
};

// Flash 上傳
var getFlashUploadObj = function() {
    if (supportFlash()) { // supportFlash 函數(shù)未提供
        var str = '<object type="application/x-shockwave-flash"></object>';
        return $(str).appendTo($('body'));
    };
    return false;
}

// 表單上傳
var getFormUpladObj = function() {
    var str = '<input name="file" type="file" class="ui-file"/>'; 
    return $(str).appendTo($('body'));
};

在以上三個(gè)函數(shù)中,如果該函數(shù)里面的 upload 對(duì)象是可用的,則讓函數(shù)返回該對(duì)象,反之返回 false,提示迭代器繼續(xù)往后面進(jìn)行迭代。

那么迭代器只需進(jìn)行下面這幾步工作:

  • 提供一個(gè)可以被迭代的方法,使得 getActiveUploadObj,getFlashUploadObj 以及 getFlashUploadObj 依照優(yōu)先級(jí)被循環(huán)迭代。
  • 如果正在被迭代的函數(shù)返回一個(gè)對(duì)象,則表示找到了正確的 upload 對(duì)象,反之如果該函數(shù)返回 false,則讓迭代器繼續(xù)工作。
var iteratorUpload = function() {
    for (var i = 0, fn; fn = arguments[i++];) {
        var upload = fn();
        if (upload !== false) {
            return upload;
        }
    }
};

var uploadObj = iteratorUpload( getActiveUploadObj, getFlashUploadObj, getFormUpladObj )

重構(gòu)代碼之后,可以看到,上傳對(duì)象的各個(gè)函數(shù)彼此分離互補(bǔ)干擾,很方便維護(hù)和擴(kuò)展,如果后期增加了 Webkit 控件上傳和 HTML5 上傳,我們就增加對(duì)應(yīng)的函數(shù)功能并在迭代器里添加:

var getWebkitUploadObj = function(){ 
 // 具體代碼略
}

var getHtml5UploadObj = function(){ 
 // 具體代碼略
}

// 依照優(yōu)先級(jí)把它們添加進(jìn)迭代器
var uploadObj = iteratorUploadObj( getActiveUploadObj, getWebkitUploadObj,
getFlashUploadObj, getHtml5UploadObj, getFormUpladObj );

總結(jié)

迭代器的使用場(chǎng)景是:對(duì)于集合內(nèi)部結(jié)果常常變化各異,我們不想暴露其內(nèi)部結(jié)構(gòu)的話,但又想讓客戶代碼透明地訪問其中的元素,這種情況下我們可以使用迭代器模式。


參考引用資料

《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》

湯姆大叔的博客——深入理解JavaScript系列

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,324評(píng)論 25 708
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評(píng)論 19 139
  • 單例模式 適用場(chǎng)景:可能會(huì)在場(chǎng)景中使用到對(duì)象,但只有一個(gè)實(shí)例,加載時(shí)并不主動(dòng)創(chuàng)建,需要時(shí)才創(chuàng)建 最常見的單例模式,...
    Obeing閱讀 2,321評(píng)論 1 10
  • 《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》作者:曾探 系統(tǒng)的介紹了各種模式,以及js中的實(shí)現(xiàn)、應(yīng)用,以及超大量高質(zhì)...
    undefinedR閱讀 896評(píng)論 0 10
  • 今天早上學(xué)?;I劃已久的體育節(jié)拉開了帷幕,我因去參加城關(guān)地區(qū)教育年報(bào)填報(bào)工作,孩子們的壘球操展示賽也沒看上,...
    方圓_22cf閱讀 175評(píng)論 0 0

友情鏈接更多精彩內(nèi)容