轉自: http://www.cnblogs.com/Darren_code/archive/2011/08/31/JavascripDesignPatterns.html
一直都在考慮這個月分享大家什麼東西最好,原計劃是打算寫一些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(); // 100010 result(); // 100111 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 //code5 },6 init:function(){7 //code8 }9 }
或者
1 var functionGroup =newfunction myGroup(){2 this.name ='Darren';3 this.getName =function(){4 returnthis.name5 }6 this.method1 =function(){}7 ...8 }
工廠(Factory)模式:提供一個建立一系列相關或相互依賴對象的介面,而無需指定他們具體的類。
工廠就是把成員對象的建立工作轉交給一個外部對象,好處在於消除對象之間的耦合(何為耦合。就是相互影響)。通過使用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(){