單體模式的優勢用了這麼久的單體模式,竟全然不知!用它具體有哪些好處呢? 1.可以用它來劃分命名空間(這個就是就是經常用的了) 2.利用分支技術來封裝瀏覽器之間的差異(這個還真沒用過,挺新鮮) 3.藉助單體模式,可以把程式碼群組織的更為一致,方便閱讀與維護(這個也用過了) 最基本的單體模式最簡單的單體其實就是一個對象字面量。它把一批有一定關聯的方法和屬性群組織在一起。 複製代碼var Singleton={ name: nimei, age: 2, walk: function(){ ... }, eat: function(){ ... }}複製代碼這個對象可以被修改。你可以添加屬性和方法。你也可以用delete運算子刪除現有成員。這實際上違背了物件導向設計的一條原則:類可以被擴充,但不應該被修改。如果某些變數需要保護,那麼可以將其定義在閉包中。 對象字面量只是建立單體的方法之一。也並非所有的對象字面量都是單體,那些只是用來模仿關聯陣列或容納資料的對象字面量顯然不是單體。 用作劃分命名空間防止全域聲明的修改複製代碼/*using a namespace*/var common = { findProduct: function(){ }, ...}var findProduct = true;複製代碼這樣以來,即使我們在外面聲明了相同的變數,也能在一定程度上防止findProduct的被修改。 防止其它來原始碼的修改現在網頁上的JavaScript代碼往往不止用一個來源,什麼庫代碼、廣告代碼和徽章代碼。為了避免與自己代碼的衝突,可以定義一個包含自己所有代碼的對象。 複製代碼var JChen = {};JChen.Common = { //A singleton with common methods used by all objects and modules}JChen.ErrorCodes = { //An object literal used to store data}JChen.PageHandler = { //A singleton with page specific methods and attributes.}複製代碼用作專用程式碼封裝裝器在擁有許多網頁的網站中,有些代碼是所有網頁都要用到的,他們通常被存放在獨立的檔案中;而有些代碼則是某個網頁專用的,不會被用到其他地方。最好把這兩種代碼分別封裝在自己的單體對象中。 我們經常要用Javascript為表單添加功能。出於平穩退化方面的考慮,通常先建立一個不依賴於Javascript的、使用普通提交機制完成任務的純HTML網頁。 複製代碼JChen.RegPage = { FORM_ID: 'reg-form', OUTPUT_ID: 'reg-result', handleSubmit: function(e){ e.preventDefault(); //stop the normal form submission var data = {}; var inputs = JChen.RegPage.formEl.getElementByTagName('input'); for(var i=0, len=inputs.length; i<len; i++){ data[inputs[i].name] = inputs[i].value; } JChen.RegPage.sendRegistration(data); }, sendRegistration: function(data){ //make an xhr request and call displayResult() when response is recieved ... }, displayResult: function(response){ JChen.RegPage.outputEl.innerHTML = response; }, init: function(){ JChen.RegPage.formEl =$(JChen.RegPage.Form_ID); JChen.RegPage.outputEl = $(JChen.RegPage.OUTPUT_ID); //hijack the form submission addEvent(JChen.RegPage.formEl, 'submit', JChen.RegPage.handleSubmit); }}//invoke initialization method after the page loadaddLoadEvent(JChen.RegPage.init);複製代碼擁有私人成員的單體第3章介紹的建立類的私人成員的做法的缺點在於它們比較耗費記憶體,因為每個執行個體都有方法的一份新副本。 不過,由於單體對象只會被執行個體化一次,因此為其定義私用方法是不必考慮記憶體方面的問題。 使用底線標記法這個方法就不介紹了,太簡單了。 使用閉包複製代碼JChen.DataParser = (function(){ //private members var whitespaceRegex = /\s+/; function stripWhitespace(str){ return str.replace(whitespaceRegex, ''); } function stringSplit(str, delimiter){ return str.split(delimiter); } return { //public members stringToArray: function(str, delimiter, stripWS){ if(stripWS){ str = stripWhitespace(str); } var outputArray = stringSplit(str, delimiter); return outputArray; } }})();複製代碼這種單體模式又稱模組模式,指的是它可以把一批相關的方法和屬性群組織為模組並起到劃分命名空間的作用。 現在這些私用方法和屬性可以直接用其名來訪問,不必在其前面加上this.或JChen.DataParser,這些首碼只適用於訪問單體對象的公用成員。 惰性執行個體化技術前面所講的單體模式又一個共同點:單體對象都是在指令碼載入時被建立出來。對於資源密集的或配置開銷甚大的單體,更合理的做法是將其執行個體化延遲到需要使用他的時候。 這種技術就是惰性載入(lazy loading)。 實現步驟如下: 1.將所有代碼移到constructor方法中 2.全權控制調用時機(正是getInstance所要做的) 複製代碼JChen.lazyLoading = (function(){ var uniqInstance; function constructor(){ var attr = false; function method(){ } return { attrp: true, methodp: function(){ } } } return { getInstance: function(){ if(!uniqInstance){ uniqInstance = constructor(); } return uniqInstance; } }})();複製代碼分支技術分支是一種用來把瀏覽器間的差異封裝在運行期間進行設定的動態方法中的技術。 比如說,如果網站中要頻繁使用xhr,每次調用都要再次運行瀏覽器嗅探代碼,這樣會嚴重缺乏效率。 更有效做法是在指令碼載入時一次性地確定針對瀏覽器的代碼。這正是分支技術所做的事情。 當然,分支技術並不總是更高效的選擇,在兩個或者多個分支中只有一個分支被用到了,其他分支就佔用了記憶體。 在考慮是否使用分支技術的時候,必須在縮短時間和佔用更多記憶體這一利一弊之間權衡一下。 下面利用分支技術實現XHR: 複製代碼var XHR = (function(){ var standard = { createXhrObj: function(){ return new XMLHttpRequest(); } }; var activeXNew = { createXhrObj: function(){ return new ActiveXObject('Msxml2.XMLHTTP'); } }; var activeXOld = { createXhrObj: function(){ return new ActiveXObject('Microsoft.XMLHTTP'); } }; var testObj; try{ testObj = standard.createXhrObj(); return testObj; }catch(e){ try{ testObj = activeXNew.createXhrObj(); return testObj; }catch(e){ try{ testObj = activeXOld.createXhrObj(); return testObj; }catch(e){ throw new Error('No XHR object found in this environment.'); } } }})();