javascript設計模式——迭代器模式

来源:http://www.cnblogs.com/xiaohuochai/archive/2017/12/14/8030802.html
-Advertisement-
Play Games

[1]迭代器實現 [2]迭代器分類 [3]迭代類數組 [4]倒序迭代器 [5]中止迭代器 [6]文件上傳 ...


前面的話

  迭代器模式是指提供一種方法順序訪問一個聚合對象中的各個元素,而又不需要暴露該對象的內部表示。迭代器模式可以把迭代的過程從業務邏輯中分離出來,在使用迭代器模式之後,即使不關心對象的內部構造,也可以按順序訪問其中的每個元素。迭代器模式是一種相對簡單的模式,簡單到很多時候都不認為它是一種設計模式。本文將詳細介紹迭代器模式

 

迭代器實現

  迭代器模式無非就是迴圈訪問聚合對象中的各個元素。比如jQuery中的$.each函數,其中回調函數中的參數i為當前索引,n為當前元素,代碼如下:

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

  現在來自己實現一個each函數,each函數接受2個參數,第一個為被迴圈的數組,第二個為迴圈中的每一步後將被觸發的回調函數

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

each( [ 1, 2, 3 ], function( i, n ){
    alert ( [ i, n ] );
});

 

迭代器分類

  迭代器可以分為內部迭代器和外部迭代器,它們有各自的適用場景

【內部迭代器】

  剛剛編寫的each函數屬於內部迭代器,each函數的內部已經定義好了迭代規則,它完全接手整個迭代過程,外部只需要一次初始調用

  內部迭代器在調用的時候非常方便,外界不用關心迭代器內部的實現,跟迭代器的交互也僅僅是一次初始調用,但這也剛好是內部迭代器的缺點。由於內部迭代器的迭代規則已經被提前規定,上面的each函數就無法同時迭代2個數組了

  比如現在有個需求,要判斷2個數組裡元素的值是否完全相等,如果不改寫each函數本身的代碼,能夠入手的地方似乎只剩下each的回調函數了,代碼如下:

var compare = function( ary1, ary2 ){
    if ( ary1.length !== ary2.length ){
        throw new Error ( 'ary1 和ary2 不相等' );
    }
    each( ary1, function( i, n ){
        if ( n !== ary2[ i ] ){
            throw new Error ( 'ary1 和ary2 不相等' );
        }
    });
    alert ( 'ary1 和ary2 相等' );
};
compare( [ 1, 2, 3 ], [ 1, 2, 4 ] ); // throw new Error ( 'ary1 和ary2 不相等' );

【外部迭代器】

  外部迭代器必須顯式地請求迭代下一個元素。外部迭代器增加了一些調用的複雜度,但相對也增強了迭代器的靈活性,可以手工控制迭代的過程或者順序

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
    }
};

  下麵來改寫compare函數:

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 相等

  外部迭代器雖然調用方式相對複雜,但它的適用面更廣,也能滿足更多變的需求。內部迭代器和外部迭代器在實際生產中沒有優劣之分,究竟使用哪個要根據需求場景而定

 

迭代類數組

  迭代器模式不僅可以迭代數組,還可以迭代一些類數組的對象。比如arguments、{"0":'a',"1":'b'}等。無論是內部迭代器還是外部迭代器,只要被迭代的聚合對象擁有length屬性而且可以用下標訪問,那它就可以被迭代

  在javascript中,for in語句可以用來迭代普通字面量對象的屬性。jQuery中提供了$.each函數來封裝各種迭代行為

$.each = function( obj, callback ) {
    var value,
    i = 0,
    length = obj.length,
    isArray = isArraylike( obj );
    if ( isArray ) { // 迭代類數組
        for ( ; i < length; i++ ) {
            value = callback.call( obj[ i ], i, obj[ i ] );
            if ( value === false ) {
                break;
            }
        }
    } else {
        for ( i in obj ) { // 迭代object 對象
            value = callback.call( obj[ i ], i, obj[ i ] );
            if ( value === false ) {
                break;
            }
        }
    }
    return obj;
};

 

倒序迭代器

  迭代器模式提供了迴圈訪問一個聚合對象中每個元素的方法,但它沒有規定以順序、倒序還是中序來迴圈遍歷聚合對象

  下麵實現一個倒序訪問的迭代器

var reverseEach = function( ary, callback ){
    for ( var l = ary.length - 1; l >= 0; l-- ){
        callback( l, ary[ l ] );
    }
};

reverseEach( [ 0, 1, 2 ], function( i, n ){
    console.log( n ); // 分別輸出:2, 1 ,0
});

 

中止迭代器

  迭代器可以像普通for迴圈中的break一樣,提供一種跳出迴圈的方法。在jQuery的each函數里有這樣一句:

if(value===false){
  break;
}

  這句代碼的意思是,約定如果回調函數的執行結果返回false,則提前終止迴圈。下麵把之前的each函數改寫一下:

var each = function( ary, callback ){
    for ( var i = 0, l = ary.length; i < l; i++ ){
        if ( callback( i, ary[ i ] ) === false ){ // callback 的執行結果返回false,提前終止迭代
            break;
        }
    }
};

each( [ 1, 2, 3, 4, 5 ], function( i, n ){
    if ( n > 3 ){ // n 大於3 的時候終止迴圈
        return false;
    }
    console.log( n ); // 分別輸出:1, 2, 3
});

 

文件上傳

  下麵是一段關於文件上傳的代碼,目的是根據不同的瀏覽器獲取相應的上傳組件對象:

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') );
    }
  }
};

  在不同的瀏覽器環境下,選擇的上傳方式是不一樣的。因為使用瀏覽器的上傳控制項進行上傳速度快,可以暫停和續傳,所以首先會優先使用控制項上傳。如果瀏覽器沒有安裝上傳控制項,則使用Flash上傳,如果連Flash也沒安裝,那就只好使用瀏覽器原生的表單上傳了

  上面的代碼為了得到一個upload對象,getUploadObj函數裡面充斥了try,catch以及if條件分支。缺點顯而易見,很難閱讀,且嚴重違反開閉原則。在開發和調試過程中,需要來回切換不同的上傳方式,如果增加了一些另外的上傳方式,比如,HTML5上傳,這時唯一的辦法是繼續往getUploadObj函數里增加條件分支

  目前一共有3種可能的上傳方式,但不知道目前正在使用的瀏覽器支持哪幾種。把每種獲取upload對象的方法都封裝在各自的函數里,然後使用一個迭代器,迭代獲取這些upload對象,直到獲取到一個可用的為止

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

var getFlashUploadObj = function(){
  if ( supportFlash() ){    // 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') );
};

  在getActiveUploadObj、getFlashUploadObj、getFormUpladObj這3個函數中都有同一個約定:如果該函數裡面的upload對象是可用的,則讓函數返回該對象,反之返回false,提示迭代器繼續往後面進行迭代

  所以我們的迭代器只需進行下麵這兩步工作:1、提供一個可以被迭代的方法,使得getActiveUploadObj,getFlashUploadObj以及getFlashUploadObj依照優先順序被迴圈迭代;2、如果正在被迭代的函數返回一個對象,則表示找到了正確的upload對象,反之如果該函數返回false,則讓迭代器繼續工作

  迭代器代碼如下:

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 );

  重構代碼之後,獲取不同上傳對象的方法被隔離在各自的函數里互不幹擾,try、catch和if分支不再糾纏在一起,使得可以很方便地的維護和擴展代碼。比如,給上傳項目增加了Webkit控制項上傳和HTML5上傳,要做的僅僅是下麵一些工作

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

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

  依照優先順序把它們添加進迭代器:

var uploadObj=iteratorUploadObj(getActiveUploadObj,getWebkitUploadObj,getFlashUploadObj,getHtml5UploadObj,getFormUpladObj);

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 經過前兩篇破解教程,想必大家也是明白了破解的簡單流程了。 先對APP進行試用,瞭解APP運行的大概流程,之後從APP中找出關鍵字(一般的關鍵字差不多都是支付失敗),之後使用Androidkiller進行反編譯,對關鍵字或者關鍵字的Unicode進行搜索,之後,從搜索的結果中找出關鍵的smail文件, ...
  • .NET的框架造多了,今天就為IOS造一個了,本文介紹Sagit框架的起緣故事及簡介... ...
  • UIButton * nameButton = [UIButton buttonWithType:UIButtonTypeCustom]; nameButton.frame = CGRectMake(0, 200, self.view.frame.size.width, 100); nameButt ...
  • Gallery是一個內部元素控制項,可以水平滾動,並且可以把當前選擇的子元素定位在它中心的佈局組件;畫廊Gallery一般用來顯示可左右移動圖片的列表(具體請看實例)... ...
  • 最近在做app登錄的時候,因為需要支持國外手機號註冊和登錄,所以就涉及到國際電話區號的選擇。在github上面找了一下,國家名稱基本都是只有英文版本,而手動的去把中文一個個加上實在是一件費時費力的事情,所以就寫了一段簡單的java代碼,抓取了某快遞網站的數據轉換成json格式,以下是處理後的數據 然 ...
  • 一.動態規劃原理<!--?xml version="1.0" encoding="UTF-8"?--> 多階段決策問題中,各個階段採取的決策,一般來說是與時間有關的,決策依賴於當前狀態,又隨即引起狀態的轉移,一個決策序列就是在變化的狀態中產生出來的,故有“動態”的含義,稱這種解決多階段決策最優化問題 ...
  • jQuery 的動畫方法(animate)支持各種屬性的過渡,但是預設並不支持色彩的過渡,該插件正是來補足這一點! PS: 該插件支持 RGBA 顏色的過渡,但是請註意,IE8以下的版本不支持 RGBA 顏色。 color backgroundColor borderColor borderBott ...
  • 眾所周知,JavaScript預設採用 進行編碼,允許使用 形式表示一個字元,其中 是字元的Unicode碼點。ES6擴展了unicode的表示 1.字元串新增方法 String.codePointAt(index):正確處理4個位元組存儲的字元,返回一個字元的碼點; String.fromCodeP ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...