標籤:form 安裝 append load 元素 err 調用 false 互動
迭代器模式是指提供一種方法順序訪問一個彙總對象中的各個元素,而又不需要暴露該對象的內部表示。迭代器模式可以把迭代的過程從商務邏輯中分離出來,在使用迭代器模式之後,即使不關心對象的內部構造,也可以按順序訪問其中的每個元素。
一、內部迭代器和外部迭代器
1. 內部迭代器
內部迭代器函數的內部已經定義好了迭代規則,它完全接手整個迭代過程,外部只需要一次初始調用。
內部迭代器在調用的時候非常方便,外界不用關心迭代器內部的實現,跟迭代器的互動也僅僅是一次初始調用,但這也剛好是內部迭代器的缺點。
var each = function( ary, callback ){ for ( var i = 0, l = ary.length; i < l; i++ ){ callback.call( ary[i], i, ary[ i ] ); // 把下標和元素當作參數傳給callback 函數 } }; var compare = function( iterator1, iterator2 ){ while( !iterator1.isDone() && !iterator2.isDone() ){ if ( iterator1.getCurrItem() !== iterator2.getCurrItem() ){ throw new Error ( ‘iterator1 和iterator2 不相等‘ ); } iterator1.next(); iterator2.next(); } alert ( ‘iterator1 和iterator2 相等‘ ); } var iterator1 = Iterator( [ 1, 2, 3 ] ); var iterator2 = Iterator( [ 1, 2, 3 ] ); compare( iterator1, iterator2 ); // 輸出:iterator1 和iterator2 相等
2. 外部迭代器
外部迭代器必須顯式地請求迭代下一個元素。
外部迭代器增加了一些調用的複雜度,但相對也增強了迭代器的靈活性,我們可以手工控制迭代的過程或者順序。
//外部迭代器 var Iterator = function( obj ){ var current = 0; var next = function(){ current += 1; }; var isDone = function(){ return current >= obj.length; }; var getCurrItem = function(){ return obj[ current ]; }; return { next: next, isDone: isDone, getCurrItem: getCurrItem } }; //比較數組是否相同 var compare = function( iterator1, iterator2 ){ while( !iterator1.isDone() && !iterator2.isDone() ){ if ( iterator1.getCurrItem() !== iterator2.getCurrItem() ){ throw new Error ( ‘iterator1 和iterator2 不相等‘ ); } iterator1.next(); iterator2.next(); } alert ( ‘iterator1 和iterator2 相等‘ ); } var iterator1 = Iterator( [ 1, 2, 3 ] ); var iterator2 = Iterator( [ 1, 2, 3 ] ); compare( iterator1, iterator2 ); // 輸出:iterator1 和iterator2 相等 二、迭代類數組對象和字面量對象
無論是內部迭代器還是外部迭代器,只要被迭代的彙總對象擁有 length 屬性而且可以用下標訪問,那它就可以被迭代。如數組、對象、arguments。
三、迭代器模式的應用舉例
下面的方法是根據不同的瀏覽器擷取相應的上傳組件對象,因為使用瀏覽器的上傳控制項進行上傳速度快,可以暫停和續傳,所以我們首先會優先使用控制項上傳。如果瀏覽器沒有安裝上傳控制項,則使用 Flash 上傳,如果連 Flash 也沒安裝,那就只好使用瀏覽器原生的表單上傳了。
var getUploadObj = function(){ try{ // IE 上傳控制項 return new ActiveXObject("TXFTNActiveX.FTNUpload"); }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‘) ); } } };
getUploadObj 函數裡面充斥了 try,catch以及 if 條件分支。缺點是顯而易見的。第一是很難閱讀,第二是嚴重違反開閉原則。 在開發和調試過程中,我們需要來回切換不同的上傳方式,每次改動都相當痛苦。後來我們還增加支援了一些另外的上傳方式,比如,HTML5 上傳,這時候唯一的辦法是繼續往 getUploadObj 函數裡增加條件分支。
我們把每種擷取 upload 對象的方法都封裝在各自的函數裡,然後使用一個迭代器,迭代擷取這些 upload 對象,直到擷取到一個可用的為止:
// IE 上傳控制項 var getActiveUploadObj = function(){ try{ return new ActiveXObject( "TXFTNActiveX.FTNUpload" ); }catch(e){ return false; } }; // supportFlash 函數未提供 var getFlashUploadObj = function(){ if ( supportFlash() ){ 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‘) ); }; //迭代器 var iteratorUploadObj = function(){ for ( var i = 0, fn; fn = arguments[ i++ ]; ){ var uploadObj = fn(); if ( uploadObj !== false ){ return uploadObj; } } }; var uploadObj = iteratorUploadObj( getActiveUploadObj, getFlashUploadObj, getFormUpladObj );
在 getActiveUploadObj 、 getFlashUploadObj 、 getFormUpladObj 這 3 個函數中都有同一個約定:如果該函數裡面的 upload 對象是可用的,則讓函數返回該對象,反之返回 false ,提示迭代器繼續往後面進行迭代。
JavaScript設計模式與開發實踐 迭代器模式