<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      前端攻略系列(三) - javascript 設計模式(文章很長,請自備瓜子,水果和眼藥水)

      2011-08-31 23:55  聶微東  閱讀(76425)  評論(158)    收藏  舉報

        一直都在考慮這個月分享大家什么東西最好,原計劃是打算寫一些HTML5中JS方面的內容或者是AJAX方面的,可是自己由于表達能力,時間,還有個人工作方面的問題,這個還是等下個月再做分享吧^.^。

        老規矩,開始正文以前先交代自己寫這篇文章的目的和一些注意事項:

        1.首先本人一直從事前端開發,所以除了JavaScript其他的語言了解不深,所以文章只會以JavaScript語言的角度去論證;

        2.其實我個人在項目用過的模式也不多,對模式的概念的理解也沒有那么抽象,所以最近在面試中如果面試官問到與模式相關的問題,自己感覺在對答過程中很郁悶,很多東西表達不清楚,于是就找了些相關資料,才會有這篇文章分享;

        3.JavaScript模式與前端的工作和成長密不可分,因為這確實不是一個簡單的話題,所以我只能盡力用簡單表達和例子闡明,而且園子里有很多的高手,所以希望大家踴躍發言(由于水平有限,請大家多多指教,希望嘴下留情);

        4.由于這篇文章更多的只是想起到一個介紹和講解的作用,并不打算對每種模式進行細致的分析,所以每種模式只用到一個至二個例子,可能會造成這個例子的表達并不是最優的或者不夠全面,如果各位看官覺得不過癮,可以再去查找相關資料;

        5.做任何事都需要堅持,寫博客也是一樣,嘿嘿,每月至少一篇(文章確實較長,希望能對朋友們有所幫助,重點部分在前言中有介紹,大家可以選擇感興趣的模式進行深入)。

        6.歡迎轉載,不過請注明出處,謝謝。
       

      了解JavaScript設計模式我們需要知道的一些必要知識點:(內容相對基礎,高手請跳過)

        閉包:關于閉包這個月在園子里有幾篇不錯的分享了,在這我也從最實際的地方出發,說說我的理解。

          1.閉包最常用的方式就是返回一個內聯函數(何為內聯函數?就是在函數內部聲明的函數);

          2.在JavaScript中有作用域和執行環境的問題,在函數內部的變量在函數外部是無法訪問的,在函數內部卻可以得到全局變量。由于種種原因,我們有時候需要得到函數內部的變量,可是用常規方法是得不到的,這時我們就可以創建一個閉包,用來在外部訪問這個變量。

          3.閉包的用途 主要就是上一點提到的讀取函數內部變量,還有一個作用就是可以使這些變量一直保存在內存中。

          4.使用閉包要注意,由于變量被保存在內存中,所以會對內存造成消耗,所以不能濫用閉包。解決方法是 在退出函數之前,將不使用的局部變量全部刪除。

          最后還是上一套閉包的代碼吧,這樣更直觀。

       1   function f(){
      2   var n = 999;
      3   function f1(){
      4    alert(n+=1);
      5    }
      6    return f1;
      7   }
      8   var result = f();
      9   result(); // 1000
      10   result(); // 1001
      11   result(); // 1002


        封裝:通過將一個方法或者屬性聲明為私用的,可以讓對象的實現細節對其他對象保密以降低對象之間的耦合程度,可以保持數據的完整性并對其修改方式加以約束,這樣可以是代碼更可靠,更易于調試。封裝是面向對象的設計的基石。

        盡管JavaScript是一門面向對象的語言,可它并不具備將成員聲明為公用或私用的任何內部機制,所以我們只能自己想辦法實現這種特性。下面還是通過一套完整的代碼去分析,介紹什么是私有屬性和方法,什么是特權屬性和方法,什么是屬性和方法,什么是靜態屬性和方法。

        私有屬性和方法:函數有作用域,在函數內用var 關鍵字聲明的變量在外部無法訪問,私有屬性和方法本質就是你希望在對象外部無法訪問的變量。

        特權屬性和方法:創建屬性和方法時使用的this關鍵字,因為這些方法定義在構造器的作用域中,所以它們可以訪問到私有屬性和方法;只有那些需要直接訪問私有成員的方法才應該被設計為特權方法。

        共有屬性和方法:直接鏈在prototype上的屬性和方法,不可以訪問構造器內的私有成員,可以訪問特權成員,子類會繼承所有的共有方法。

        共有靜態屬性和方法:最好的理解方式就是把它想象成一個命名空間,實際上相當于把構造器作為命名空間來使用。

       

       1   /* -- 封裝 -- */
      2   var _packaging =function(){
      3   //私有屬性和方法
      4   var name ='Darren';
      5   var method1 =function(){
      6    //...
      7   }
      8   //特權屬性和方法
      9   this.title ='JavaScript Design Patterns' ;
      10   this.getName =function(){
      11    return name;
      12   }
      13   }
      14   //共有靜態屬性和方法
      15   _packaging._name ='Darren code';
      16   _packaging.alertName =function(){
      17   alert(_packaging._name);
      18   }
      19   //共有屬性和方法
      20   _packaging.prototype = {
      21   init:function(){
      22   //...
      23   }
      24   }


        繼承:繼承本身就是一個抽象的話題,在JavaScript中繼承更是一個復雜的話題,因為JavaScript想要實現繼承有兩種實現方式,分別是類式繼承和原型式繼承,每種實現的方式都需要采取不少措施,下面本人通過分析例子的方式講解JavaScript中這個很重要的話題。

       1   /* -- 類式繼承 -- */
      2   //先聲明一個超類
      3   function Person(name){
      4     this.name = name;
      5   }
      6   //給這個超類的原型對象上添加方法 getName
      7   Person.prototype.getName =function(){
      8   returnthis.name;
      9   }
      10   //實例化這個超類
      11   var a =new Person('Darren1')
      12   alert(a.getName());
      13   //再聲明類
      14   function Programmer(name,sex){
      15   //這個類中要調用超類Person的構造函數,并將參數name傳給它
      16   Person.call(this,name);
      17   this.sex = sex;
      18   }
      19   //這個子類的原型對象等于超類的實例
      20   Programmer.prototype =new Person();
      21   //因為子類的原型對象等于超類的實例,所以prototype.constructor這個方法也等于超類構造函數,你可以自己測試一下,如果沒這一步,alert(Programmer.prototype.constructor),這個是Person超類的引用,所以要從新賦值為自己本身
      22   Programmer.prototype.constructor = Programmer;
      23   //子類本身添加了getSex 方法
      24   Programmer.prototype.getSex =function(){
      25   returnthis.sex;
      26   }
      27   //實例化這個子類
      28   var _m =new Programmer('Darren2','male');
      29   //自身的方法
      30   alert(_m.getSex());
      31   //繼承超類的方法
      32   alert(_m.getName());

        代碼都不難,只要對 原型鏈 有基礎就能理解。類式繼承模式是JavaScript繼承主要的模式,幾乎所有用面向對象方式編寫的JavaScript代碼中都用到了這種繼承,又因為在各種流行語言中只有JavaScript使用原型式繼承,因此最好還是使用類式繼承。可是要熟悉JavaScript語言,原型繼承也是我們必須所了解的,至于在項目中是否使用就得看個人編碼風格了。

       1   /* -- 原型式繼承 -- */
      2   //clone()函數用來創建新的類Person對象
      3   var clone =function(obj){
      4 var _f =function(){};
      5   //這句是原型式繼承最核心的地方,函數的原型對象為對象字面量
      6   _f.prototype = obj;
      7   returnnew _f;
      8   }
      9   //先聲明一個對象字面量
      10   var Person = {
      11   name:'Darren',
      12   getName:function(){
      13   returnthis.name;
      14   }
      15   }
      16   //不需要定義一個Person的子類,只要執行一次克隆即可
      17   var Programmer = clone(Person);
      18   //可以直接獲得Person提供的默認值,也可以添加或者修改屬性和方法
      19   alert(Programmer.getName())
      20   Programmer.name ='Darren2'
      21   alert(Programmer.getName())
      22
      23   //聲明子類,執行一次克隆即可
      24   var Someone = clone(Programmer);


        ------------------------------------------  正文開始了,我是分割線  ------------------------------------------
       
        前言:

        JavaScript設計模式的作用 - 提高代碼的重用性,可讀性,使代碼更容易的維護和擴展。

         

        1.單體模式,工廠模式,橋梁模式個人認為這個一個優秀前端必須掌握的模式,對抽象編程和接口編程都非常有好處。

        2.裝飾者模式和組合模式有很多相似的地方,它們都與所包裝的對象實現同樣的接口并且會把任何方法的調用傳遞給這些對象。裝飾者模式和組合模式是本人描述的較吃力的兩個模式,我個人其實也沒用過,所以查了很多相關資料和文檔,請大家海涵。

        3.門面模式是個非常有意思的模式,幾乎所有的JavaScript庫都會用到這個模式,假如你有逆向思維或者逆向編程的經驗,你會更容易理解這個模式(聽起來有挑戰,其實一接觸你就知道這是個很簡單的模式);還有配置器模式得和門面模式一塊拿來說,這個模式對現有接口進行包裝,合理運用可以很多程度上提高開發效率。這兩個模式有相似的地方,所以一塊理解的話相信都會很快上手的。

        4.享元模式是一種以優化為目的的模式。

        5.代理模式主要用于控制對象的訪問,包括推遲對其創建需要耗用大量計算資源的類得實例化。

        6.觀察者模式用于對對象的狀態進行觀察,并且當它發生變化時能得到通知的方法。用于讓對象對事件進行監聽以便對其作出響應。觀察者模式也被稱為“訂閱者模式”。

        7.命令模式是對方法調用進行封裝的方式,用命名模式可以對方法調用進行參數化和傳遞,然后在需要的時候再加以執行。

        8.職責鏈模式用來消除請求的發送者和接收者之間的耦合。
       
       

        JavaScript設計模式都有哪些?

        單體(Singleton)模式: 絕對是JavaScript中最基本最有用的模式。

        單體在JavaScript的有多種用途,它用來劃分命名空間。可以減少網頁中全局變量的數量(在網頁中使用全局變量有風險);可以在多人開發時避免代碼的沖突(使用合理的命名空間)等等。

        在中小型項目或者功能中,單體可以用作命名空間把自己的代碼組織在一個全局變量名下;在稍大或者復雜的功能中,單體可以用來把相關代碼組織在一起以便日后好維護。  

        使用單體的方法就是用一個命名空間包含自己的所有代碼的全局對象,示例:

      1   var functionGroup = {
      2     name:'Darren',
      3     method1:function(){
      4       //code
      5     },
      6     init:function(){
      7       //code
      8     }
      9   }

        或者

      1   var functionGroup  =newfunction myGroup(){
      2     this.name ='Darren';
      3     this.getName =function(){
      4       returnthis.name
      5     }
      6     this.method1 =function(){}
      7     ...
      8   }

         

        工廠(Factory)模式:提供一個創建一系列相關或相互依賴對象的接口,而無需指定他們具體的類。

        工廠就是把成員對象的創建工作轉交給一個外部對象,好處在于消除對象之間的耦合(何為耦合?就是相互影響)。通過使用工廠方法而不是new關鍵字及具體類,可以把所有實例化的代碼都集中在一個位置,有助于創建模塊化的代碼,這才是工廠模式的目的和優勢。

        舉個例子:你有一個大的功能要做,其中有一部分是要考慮擴展性的,那么這部分代碼就可以考慮抽象出來,當做一個全新的對象做處理。好處就是將來擴展的時候容易維護 - 只需要操作這個對象內部方法和屬性,達到了動態實現的目的。非常有名的一個示例 - XHR工廠

       1   var XMLHttpFactory =function(){};      //這是一個簡單工廠模式
      2   XMLHttpFactory.createXMLHttp =function(){
      3     var XMLHttp = null;
      4     if (window.XMLHttpRequest){
      5       XMLHttp = new XMLHttpRequest()
      6     }elseif (window.ActiveXObject){
      7       XMLHttp = new ActiveXObject("Microsoft.XMLHTTP")
      8     }
      10   return XMLHttp;
      11   }
      12   //XMLHttpFactory.createXMLHttp()這個方法根據當前環境的具體情況返回一個XHR對象。
      13   var AjaxHander =function(){
      14     var XMLHttp = XMLHttpFactory.createXMLHttp();
      15     ...
      16   }


        工廠模式又區分簡單工廠模式和抽象工廠模式,上面介紹的是簡單工廠模式,這種模式用的更多也更簡單易用。抽象工廠模式的使用方法就是 - 先設計一個抽象類,這個類不能被實例化,只能用來派生子類,最后通過對子類的擴展實現工廠方法。 示例:

       1   var XMLHttpFactory =function(){};      //這是一個抽象工廠模式
      2   XMLHttpFactory.prototype = {
      3   //如果真的要調用這個方法會拋出一個錯誤,它不能被實例化,只能用來派生子類
      4   createFactory:function(){
      5   thrownew Error('This is an abstract class');
      6   }
      7   }
      8   //派生子類,文章開始處有基礎介紹那有講解繼承的模式,不明白可以去參考原理
      9   var XHRHandler =function(){
      10   XMLHttpFactory.call(this);
      11   };
      12   XHRHandler.prototype =new XMLHttpFactory();
      13   XHRHandler.prototype.constructor = XHRHandler;
      14   //重新定義createFactory 方法
      15   XHRHandler.prototype.createFactory =function(){
      16   var XMLHttp =null;
      17   if (window.XMLHttpRequest){
      18   XMLHttp =new XMLHttpRequest()
      19   }elseif (window.ActiveXObject){
      20   XMLHttp =new ActiveXObject("Microsoft.XMLHTTP")
      21   }
      22   return XMLHttp;
      23   }

        

        橋接(bridge)模式:在實現API的時候,橋梁模式灰常有用。在所有模式中,這種模式最容易立即付諸實施。

        橋梁模式可以用來弱化它與使用它的類和對象之間的耦合,就是將抽象與其實現隔離開來,以便二者獨立變化;這種模式對于JavaScript中常見的時間驅動的編程有很大益處,橋梁模式最常見和實際的應用場合之一是時間監聽器回調函數。先分析一個不好的示例:

       

      1   element.onclick =function(){
      2   new setLogFunc();
      3   };

       

        為什么說這個示例不好,因為從這段代碼中無法看出那個LogFunc方法要顯示在什么地方,它有什么可配置的選項以及應該怎么去修改它。換一種說法就是,橋梁模式的要訣就是讓接口“可橋梁”,實際上也就是可配置。把頁面中一個個功能都想象成模塊,接口可以使得模塊之間的耦合降低。

        掌握橋梁模式的正確使用收益的不只是你,還有那些負責維護你代碼的人。把抽象于其實現隔離開,可獨立地管理軟件的各個部分,bug也因此更容易查找。

        橋梁模式目的就是讓API更加健壯,提高組件的模塊化程度,促成更簡潔的實現,并提高抽象的靈活性。一個好的示例:

      1   element.onclick =function(){  //API可控制性提高了,使得這個API更加健壯
      2   new someFunction(element,param,callback);
      3   }

        注:橋梁模式還可以用于連接公開的API代碼和私有的實現代碼,還可以把多個類連接在一起。在文章封裝介紹的部分提到過特權方法,也是橋梁模式的一種特例。《JS設計模式》上找的示例,加深大家對這個模式的理解:

       1   //錯誤的方式
      2   //這個API根據事件監聽器回調函數的工作機制,事件對象被作為參數傳遞給這個函數。本例中并沒有使用這個參數,而只是從this對象獲取ID。
      3   addEvent(element,'click',getBeerById);
      4   function(e){
      5   var id =this.id;
      6   asyncRequest('GET','beer.url?id='+ id,function(resp){
      7   //Callback response
      8    console.log('Requested Beer: '+ resp.responseText);
      9   });
      10   }
      11
      12   //好的方式
      13   //從邏輯上分析,把id傳給getBeerById函數式合情理的,且回應結果總是通過一個毀掉函數返回。這么理解,我們現在做的是針對接口而不是實現進行編程,用橋梁模式把抽象隔離開來。
      14   function getBeerById(id,callback){
      15   asyncRequest('GET','beer.url?id='+ id,function(resp){
      16   callback(resp.responseText)
      17   });
      18   }
      19   addEvent(element,'click',getBeerByIdBridge);
      20   function getBeerByIdBridge(e){
      21   getBeerById(this.id,function(beer){
      22   console.log('Requested Beer: '+ beer);
      23   });
      24   } 

       

        裝飾者(Decorator)模式:這個模式就是為對象增加功能(或方法)。

        動態地給一個對象添加一些額外的職責。就擴展功能而言,它比生成子類方式更為靈活。

        裝飾者模式和組合模式有很多共同點,它們都與所包裝的對象實現統一的接口并且會把任何方法條用傳遞給這些對象。可是組合模式用于把眾多子對象組織為一個整體,而裝飾者模式用于在不修改現有對象或從派生子類的前提下為其添加方法。

        裝飾者的運作過程是透明的,這就是說你可以用它包裝其他對象,然后繼續按之前使用那么對象的方法來使用,從下面的例子中就可以看出。還是從代碼中理解吧:

       1   //創建一個命名空間為myText.Decorations
      2   var myText= {};
      3   myText.Decorations={};
      4   myText.Core=function(myString){
      5   this.show =function(){return myString;}
      6   }
      7   //第一次裝飾
      8   myText.Decorations.addQuestuibMark =function(myString){
      9   this.show =function(){return myString.show()+'?';};
      10   }
      11   //第二次裝飾
      12   myText.Decorations.makeItalic =function(myString){
      13   this.show =function(){return'<li>'+myString.show()+'</li>'};
      14   }
      15   //得到myText.Core的實例
      16   var theString =new myText.Core('this is a sample test String');
      17   alert(theString.show());  //output 'this is a sample test String'
      18   theString =new myText.Decorations.addQuestuibMark(theString);
      19   alert(theString.show());  //output 'this is a sample test String?'
      20   theString =new myText.Decorations.makeItalic (theString);
      21   alert(theString.show());  //output '<li>this is a sample test String</li>'

       

        從這個示例中可以看出,這一切都可以不用事先知道組件對象的接口,甚至可以動態的實現,在為現有對象增添特性這方面,裝飾者模式有極大的靈活性。

        如果需要為類增加特性或者方法,而從該類派生子類的解決辦法并不實際的話,就應該使用裝飾者模式。派生子類之所以會不實際最常見的原因是需要添加的特性或方法的數量要求使用大量子類。

       

       

        組合(Composite)模式:將對象組合成樹形結構以表示“部分-整體”的層次結構。它使得客戶對單個對象和復合對象的使用具有一致性。

        組合模式是一種專為創建Web上的動態用戶界面而量身定制的模式。使用這種模式,可以用一條命令在多個對象上激發復雜的或遞歸的行為。組合模式擅長于對大批對象進行操作。

        組合模式的好處:1.程序員可以用同樣的方法處理對象的集合與其中的特定子對象;2.它可以用來把一批子對象組織成樹形結構,并且使整棵樹都可被便利。

        組合模式適用范圍:1.存在一批組織成某處層次體系的對象(具體結構可能在開發期間無法知道);2.希望對這批對象或其中的一部分對象實話一個操作。

        其實組合模式就是將一系列相似或相近的對象組合在一個大的對象,由這個大對象提供一些常用的接口來對這些小對象進行操作,代碼可重用,對外操作簡單。例如:對form內的元素,不考慮頁面設計的情況下,一般就剩下input了,對于這些input都有name和value的屬性,因此可以將這些input元素作為form對象的成員組合起來,form對象提供對外的接口,便可以實現一些簡單的操作,比如設置某個input的value,添加/刪除某個input等等。

        這種模式描述起來比較吃力,我從《JS設計模式》上找個一個實例,大家還是看代碼吧:先創建組合對象類

       1   // DynamicGallery Class
      2   var DynamicGallery =function (id) { // 實現Composite,GalleryItem組合對象類
      3   this.children = [];
      4   this.element = document.createElement('div');
      5   this.element.id = id;
      6   this.element.className ='dynamic-gallery';
      7   }
      8   DynamicGallery.prototype = {
      9   // 實現Composite組合對象接口
      10   add: function (child) {
      11   this.children.push(child);
      12   this.element.appendChild(child.getElement());
      13   },
      14   remove: function (child) {
      15   for (var node, i =0; node =this.getChild(i); i++) {
      16   if (node == child) {
      17   this.children.splice(i, 1);
      18    break;
      19   }
      20   }
      21   this.element.removeChild(child.getElement());
      22   },
      23   getChild: function (i) {
      24   returnthis.children[i];
      25   },
      26   // 實現DynamicGallery組合對象接口
      27   hide: function () {
      28   for (var node, i =0; node =this.getChild(i); i++) {
      29   node.hide();
      30   }
      31   this.element.style.display ='none';
      32   },
      33   show: function () {
      34   this.element.style.display ='block';
      35   for (var node, i =0; node = getChild(i); i++) {
      36   node.show();
      37   }
      38   },
      39   // 幫助方法
      40   getElement: function () {
      41   returnthis.element;
      42   }
      43   }

        再創建葉對象類

       1   var GalleryImage =function (src) { // 實現Composite和GalleryItem組合對象中所定義的方法 
      2   this.element = document.createElement('img');
      3   this.element.className ='gallery-image';
      4   this.element.src = src;
      5   }
      6   GalleryImage.prototype = {
      7   // 實現Composite接口
      8   // 這些是葉結點,所以我們不用實現這些方法,我們只需要定義即可
      9   add: function () { },
      10   remove: function () { },
      11   getChild: function () { },
      12   // 實現GalleryItem接口
      13   hide: function () {
      14   this.element.style.display ='none';
      15   },
      16   show: function () {
      17   this.element.style.display ='';
      18   },
      19   // 幫助方法
      20   getElement: function () {
      21   returnthis.element;
      22   }
      23   }

        現在我們可以使用這兩個類來管理圖片:

       1   var topGallery =new DynamicGallery('top-gallery'); 
      2   topGallery.add(new GalleryImage('/img/image-1.jpg'));
      3   topGallery.add(new GalleryImage('/img/image-2.jpg'));
      4   topGallery.add(new GalleryImage('/img/image-3.jpg'));
      5   var vacationPhotos =new DyamicGallery('vacation-photos');
      6   for(var i =0, i <30; i++){
      7     vacationPhotos.add(new GalleryImage('/img/vac/image-'+ i +'.jpg'));
      8   }
      9   topGallery.add(vacationPhotos);
      10   topGallery.show();
      11   vacationPhotos.hide();

       

        門面(facade)模式:門面模式是幾乎所有JavaScript庫的核心原則
        子系統中的一組接口提供一個一致的界面,門面模式定義了一個高層接口,這個接口使得這一子系統更加容易使用,簡單的說這是一種組織性的模式,它可以用來修改類和對象的接口,使其更便于使用。

        門面模式的兩個作用:1.簡化類的接口;2.消除類與使用它的客戶代碼之間的耦合。

        門面模式的使用目的就是圖方面。

        想象一下計算機桌面上的那些快捷方式圖標,它們就是在扮演一個把用戶引導至某個地方的接口的角色,每次操作都是間接的執行一些幕后的命令。

        你在看這篇的博客的時候我就假設你已經有JavaScript的使用經驗了,那么你一定寫過或者看過這樣的代碼:

      1   var addEvent =function(el,type,fn){
      2   if(window.addEventListener){
      3   el.addEventListener(type,fn);
      4   }elseif(window.attachEvent){
      5   el.attachEvent('on'+type,fn);
      6   }else{
      7   el['on'+type] = fn;
      8   }
      9   }

       

        這個就是一個JavaScript中常見的事件監聽器函數,這個函數就是一個基本的門面,有了它,就有了為DOM節點添加事件監聽器的簡便方法。

        現在要說門面模式的精華部分了,為什么說JavaScript庫幾乎都會用這種模式類。假如現在要設計一個庫,那么最好把其中所有的工具元素放在一起,這樣更好用,訪問起來更簡便。看代碼:

       1   //_model.util是一個命名空間
      2   _myModel.util.Event = {
      3   getEvent:function(e){
      4   return e|| window.event;
      5   },
      6   getTarget:function(e){
      7   return e.target||e.srcElement;
      8   },
      9   preventDefault:function(e){
      10   if(e.preventDefault){
      11   e.preventDefault();
      12   }else{
      13   e.returnValue =false;
      14   }
      15   }
      16   };
      17   //事件工具大概就是這么一個套路,然后結合addEvent函數使用
      18   addEvent(document.getElementsByTagName('body')[0],'click',function(e){
      19   alert(_myModel.util.Event.getTarget(e));
      20   });

        個人認為,在處理游覽器差異問題時最好的解決辦法就是把這些差異抽取的門面方法中,這樣可以提供一個更一致的接口,addEvent函數就是一個例子。 

       

        適配置器(Adapter)模式:將一個類的接口轉換成客戶希望的另外一個接口。適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作,使用這種模式的對象又叫包裝器,因為他們是在用一個新的接口包裝另一個對象。

        從表面上看,它和門面模式有點相似,差別在于它們如何改變接口,門面模式展現的是一個簡化的接口,它并不提供額外的選擇,而適配器模式則要把一個接口轉換為另一個接口,它并不會濾除某些能力,也不會簡化接口。先來一個簡單的示例看看:

       1   //假如有一個3個字符串參數的函數,但是現在擁有的卻是一個包含三個字符串元素的對象,那么就可以用一個配置器來銜接二者
      2   var clientObject = {
      3   str1:'bat',
      4   str2:'foo',
      5   str3:'baz'
      6   }
      7   function interfaceMethod(str1,str2,str3){
      8     alert(str1)
      9   }
      10   //配置器函數
      11   function adapterMethod(o){
      12   interfaceMethod(o.str1, o.str2, o.str3);
      13   }
      14   adapterMethod(clientObject)
      15   //adapterMethod函數的作為就在于對interfaceMethod函數進行包裝,并把傳遞給它的參數轉換為后者需要的形式。

        適配器模式的工作機制是:用一個新的接口對現有類得接口進行包裝。

        示例:適配兩個庫。下面的例子要實現的是從Prototype庫的$函數到YUI的get方法的轉換。

       1   //先看它們在接口方面的差別
      2   //Prototype $ function
      3   function $(){
      4   var elements =new Array();
      5   for(var i=0;i<arguments.length;i++){
      6   var element = arguments[i];
      7   if(typeof element =='string'){
      8   element = document.getElementById(element);
      9   }
      10   if(typeof.length ==1) return element;
      11   elements.push(element);
      12   }
      13   return elements;
      14   }
      15   //YUI get method
      16   YAHOO.util.Dom.get =function(el){
      17   if(YAHOO.lang.isString(el)){
      18   return document.getElementById(el);
      19   }
      20   if(YAHOO.lang.isArray(el)){
      21   var c =[];
      22   for(var i=0,len=el.length;i<len;++i){
      23   c[c.length] = YAHOO.util.Dom.get(el[i]);
      24   }
      25   return c;
      26   }
      27   if(el){
      28   return el;
      29   }
      30   returnnull;
      31   }
      32   //二者區別就在于get具有一個參數,且可以是HTML,字符串或者數組;而$木有正是的參數,允許使用者傳入任意數目的參數,不管HTML還是字符串。
      33   //如果需要從使用Prototype的$函數改為使用YUI的get方法(或者相反,那么用適配器模式其實很簡單)
      34   function PrototypeToYUIAdapter(){
      35   return YAHOO.util.Dom.get(arguments);
      36   }
      37   function YUIToPrototypeAdapter(el){
      38   return $.apply(window,el instanceof Array?el:[el]);
      39   }

       

        享元(Flyweight)模式:運用共享技術有效地支持大量細粒度的對象。

        享元模式可以避免大量非常相似類的開銷。在程序設計中有時需要生成大量細粒度的類實例來表示數據。如果發現這些實例除了幾個參數外基本傷都是相同的,有時就能夠受大幅度第減少需要實例化的類的數量。如果能把這些參數移到類實例外面,在方法調用時將他們傳遞進來,就可以通過共享大幅度地減少單個實例的數目。

        從實際出發說說自己的理解吧。

       1   組成部分
      2   “享元”:抽離出來的外部操作和數據;
      3   “工廠”:創造對象的工廠;
      4   “存儲器”:存儲實例對象的對象或數組,供“享元”來統一控制和管理。
      5
      6   應用場景
      7   1. 頁面存在大量資源密集型對象;
      8   2. 這些對象具備一定的共性,可以抽離出公用的操作和數據
      9
      10   關鍵
      11   1. 合理劃分內部和外部數據。
      12   既要保持每個對象的模塊性、保證享元的獨立、可維護,又要盡可能多的抽離外部數據。
      13   2. 管理所有實例
      14   既然抽離出了外部數據和操作,那享元就必須可以訪問和控制實例對象。在JavaScript這種動態語言中,這個需求是很容易實現的:我們可以把工廠生產出的對象簡單的扔在一個數組中。為每個對象設計暴露給外部的方法,便于享元的控制。
      15
      16   優點
      17   1. 將能耗大的操作抽離成一個,在資源密集型系統中,可大大減少資源和內存占用;
      18   2. 職責封裝,這些操作獨立修改和維護;
      19
      20   缺點
      21   1. 增加了實現復雜度。
      22   將原本由一個工廠方法實現的功能,修改為了一個享元+一個工廠+一個存儲器。
      23   2. 對象數量少的情況,可能會增大系統開銷。

       

        示例:

       1   //汽車登記示例
      2   var Car =function(make,model,year,owner,tag,renewDate){
      3     this.make=make;
      4     this.model=model;
      5     this.year=year;
      6     this.owner=owner;
      7     this.tag=tag;
      8     this.renewDate=renewDate;
      9   }
      10   Car.prototype = {
      11     getMake:function(){
      12       returnthis.make;
      13     },
      14     getModel:function(){
      15       returnthis.model;
      16     },
      17     getYear:function(){
      18       returnthis.year;
      19     },
      20     transferOwner:function(owner,tag,renewDate){
      21       this.owner=owner;
      22       this.tag=tag;
      23       this.renewDate=renewDate;
      24     },
      25     renewRegistration:function(renewDate){
      26       this.renewDate=renewDate;
      27     }
      28   }
      29   //數據量小到沒多大的影響,數據量大的時候對計算機內存會產生壓力,下面介紹享元模式優化后
      30   //包含核心數據的Car類
      31   var Car=function(make,model,year){
      32     this.make=make;
      33     this.model=model;
      34     this.year=year;
      35   }
      36   Car.prototype={
      37     getMake:function(){
      38       returnthis.make;
      39     },
      40     getModel:function(){
      41       returnthis.model;
      42     },
      43     getYear:function(){
      44       returnthis.year;
      45     }
      46   }
      47   //中間對象,用來實例化Car類
      48   var CarFactory=(function(){
      49     var createdCars = {};
      50     return {
      51       createCar:function(make,model,year){
      52         var car=createdCars[make+"-"+model+"-"+year];
      53         return car ? car : createdCars[make +'-'+ model +'-'+ year] =(new Car(make,model,year));
      54       }
      55     }
      56   })();
      57   //數據工廠,用來處理Car的實例化和整合附加數據
      58   var CarRecordManager = (function() {
      59     var carRecordDatabase = {};
      60     return {
      61       addCarRecord:function(make,model,year,owner,tag,renewDate){
      62         var car = CarFactory.createCar(make, model, year);
      63         carRecordDatabase[tag]={
      64           owner:owner,
      65           tag:tag,
      66           renewDate:renewDate,
      67           car:car
      68       }
      69     },
      70       transferOwnership:function(tag, newOwner, newTag, newRenewDate){
      71         var record=carRecordDatabase[tag];
      72         record.owner = newOwner;
      73         record.tag = newTag;
      74         record.renewDate = newRenewDate;
      75       },
      76       renewRegistration:function(tag,newRenewDate){
      77         carRecordDatabase[tag].renewDate=newRenewDate;
      78       },
      79       getCarInfo:function(tag){
      80         return carRecordDatabase[tag];
      81       }
      82     }
      83   })();

        
        代理(Proxy)模式:
      此模式最基本的形式是對訪問進行控制。代理對象和另一個對象(本體)實現的是同樣的接口,可是實際上工作還是本體在做,它才是負責執行所分派的任務的那個對象或類,代理對象不會在另以對象的基礎上修改任何方法,也不會簡化那個對象的接口。

        舉一個具體的情況:如果那個對象在某個遠端服務器上,直接操作這個對象因為網絡速度原因可能比較慢,那我們可以先用Proxy來代替那個對象。
        總之對于開銷較大的對象,只有在使用它時才創建,這個原則可以為我們節省很多內存。《JS設計模式》上的圖書館示例:

       

       1   var Publication =new Interface('Publication', ['getIsbn', 'setIsbn', 'getTitle', 'setTitle', 'getAuthor', 'setAuthor', 'display']);
      2   var Book =function(isbn, title, author) {
      3    //...
      4   }
      5   // implements Publication
      6   implements(Book,Publication);
      7
      8   /* Library interface. */
      9   var Library =new Interface('Library', ['findBooks', 'checkoutBook', 'returnBook']);
      10
      11   /* PublicLibrary class. */
      12   var PublicLibrary =function(books) {
      13    //...
      14   };
      15   // implements Library
      16   implements(PublicLibrary,Library);
      17
      18   PublicLibrary.prototype = {
      19    findBooks: function(searchString) {
      20    //...
      21   },
      22   checkoutBook: function(book) {
      23    //...
      24    },
      25   returnBook: function(book) {
      26    //...
      27    }
      28   };
      29
      30   /* PublicLibraryProxy class, a useless proxy. */
      31   var PublicLibraryProxy =function(catalog) {
      32    this.library =new PublicLibrary(catalog);
      33   };
      34   // implements Library
      35   implements(PublicLibraryProxy,Library);
      36
      37   PublicLibraryProxy.prototype = {
      38    findBooks: function(searchString) {
      39    returnthis.library.findBooks(searchString);
      40   },
      41   checkoutBook: function(book) {
      42    returnthis.library.checkoutBook(book);
      43    },
      44    returnBook: function(book) {
      45    returnthis.library.returnBook(book);
      46    }
      47   };

       

        觀察者(Observer)模式:定義對象間的一種一對多的依賴關系,以便當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并自動刷新。

        觀察者模式中存在兩個角色,觀察者和被觀察者。在DOM的編程環境中的高級事件模式中,事件監聽器說到底就是一種內置的觀察者。事件處理器(handler)和時間監聽器(listener)并不是一回事,前者就是一種把事件傳給與其關聯的函數的手段,而在后者中,一個時間可以與幾個監聽器關聯,每個監聽器都能獨立于其他監聽器而改變。

       1   //使用時間監聽器可以讓多個函數相應一個事件
      2   var fn1 =function(){
      3   //code
      4   }
      5   var fn2 =function(){
      6   //code
      7   }
      8   addEvent(element,'click',fn1);
      9   addEvent(element,'click',fn2)
      10
      11   //而時間處理函數就辦不到
      12   element.onclick = fn1;
      13   element.onclick = fn2;

        觀察者模式是開發基于行為的應用程序的有力手段,前端程序員可做的就是借助一個事件監聽器替你處理各種行為,從而降低內存消耗和提高互動性能。

       

        命令(Command)模式:將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日志,以及支持可取消的操作。

        命令對象是一個操作和用來調用這個操作的對象的結合體,所有的命名對象都有一個執行操作,其用途就是調用命令對象所綁定的操作。示例:

       

       1   car Calculator={
      2   add:function(x,y){
      3    return x+y;
      4   },
      5   substract:function(x,y){
      6    return x-y;
      7   },
      8   multiply:function(x,y){
      9    return x*y;
      10   },
      11   divide:function(x,y){
      12    return x/y;
      13   }
      14   }
      15   Calculator.calc =function(command){
      16   return Calculator[command.type](command.op1,command.opd2)
      17   };
      18   Calculator.calc({type:'add',op1:1,op2:1});
      19   Calculator.calc({type:'substract',op1:5,op2:2});
      20   Calculator.calc({type:'multiply',op1:5,op2:2});
      21   Calculator.calc({type:'divide',op1:8,op2:4});

        命名模式的主要用途是把調用對象(用戶界面,API和代理等)與實現操作的對象隔離開,也就是說使對象間的互動方式需要更高的模塊化時都可以用到這種模式。

       

        職責鏈(Chain Of Responsibility)模式:為解除請求的發送者和接收者之間耦合,而使多個對象都有機會處理這個請求。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它。

        職責鏈由多個不同類型的對象組成:發送者是發出請求的對象,而接收者則是接收請求并且對其進行處理或傳遞的對象,請求本身有時也是一個對象,它封裝著與操作有關的所有數據。

        典型的流程大致是:

        1.發送者知道鏈中第一個接收者,它向這個接收者發出請求。

        2.每一個接收者都對請求進行分析,然后要么處理它,要么將其往下傳。

        3.每一個接收者知道的其他對象只有一個,即它在鏈中的下家。

        4.如果沒有任何接收者處理請求,那么請求將從鏈上離開,不同的實現對此也有不同的反應,一般會拋出一個錯誤。

       

         職責鏈模式的適用范圍:1.有多個的對象可以處理一個請求,哪個對象處理該請求運行時刻自動確定;2.想在不明確指定接收者的情況下,向多個對象中的一個提交一個請求;3.可處理一個請求的對象集合需要被動態指定。

        確實對這種模式不了解,相關資料也較少,所以代碼先不上了。看看大家對這個模式有木有什么好的理解或者能較好表達這種模式的代碼,謝謝了。

       

       

        結束語:

        1.每種模式都有自己的優缺點,所以每種模式的正確使用還得看開發人員本身的功力;

        2.就算不使用JavaScript設計模式一樣可以寫出 復雜的可使用 的代碼,可是如果你想真正了解JavaScript面向對象能力,學習提高代碼的模塊化程度﹑可維護性﹑可靠性和效率,那么合理的運用JavaScript設計模式將會是一個優秀 前端開發攻城濕 必備的能力。

        終于在8月31日發了這篇博客,字很多,辛苦大家了...

        最后,祝大家工作順利。

       

      主站蜘蛛池模板: 免费播放一区二区三区| 高清国产一区二区无遮挡| 日本乱码在线看亚洲乱码| 久久9精品区-无套内射无码| 西城区| 日韩精品一区二区三区蜜臀| 亚洲精品一区二区制服| 色多多性虎精品无码av| 日韩有码中文字幕第一页 | 欧美熟妇乱子伦XX视频| 久久er99热精品一区二区| 熟女精品色一区二区三区| 亚洲国产精品一二三四五| 亚洲中文字幕在线观看| 色爱av综合网国产精品| 亚洲一区二区精品动漫| 海伦市| 亚洲av一本二本三本| 国产亚洲精品第一综合另类灬| 国产在线观看播放av| 亚洲一区二区约美女探花| 成人亚欧欧美激情在线观看| 中文日产幕无线码一区中文 | 东京热tokyo综合久久精品| 国产午夜精品亚洲精品国产| 狂野欧美性猛交免费视频| 天天躁夜夜躁狠狠喷水| 国产精品中文字幕第一页| 亚洲精品色在线网站| 国产精品国产三级在线专区| 怡红院一区二区三区在线| 国产国拍亚洲精品永久软件| 亚洲国产精品高清久久久| 国产精品免费观看色悠悠| 国产三级精品片| 九九热在线精品视频观看| 亚洲18禁私人影院| 少妇激情一区二区三区视频| 日本中文字幕久久网站| 精品无码久久久久久尤物| 国产精品午夜福利在线观看|