首頁  >  文章  >  web前端  >  JavaScript中實作單體模式分享_javascript技巧

JavaScript中實作單體模式分享_javascript技巧

WBOY
WBOY原創
2016-05-16 16:17:13868瀏覽

單體模式作為一種軟體開發模式在眾多物件導向語言中得到了廣泛的使用,在javascript中,單體模式也是使用非常廣泛的,但是由於javascript語言擁有其獨特的物件導向方式,導致其和一些傳統物件導向語言雖然在單體模式的想法上是一致的,但是實現起來還是有差異的。

首先來看看傳統物件導向語言對於單體模式的定義:單體模式是只能被實例化一次並且可以透過一個眾所周知的存取點來存取的類別。這個定義有兩點突顯了傳統物件導向語言的特徵,即類別和實例化,所以對於傳統物件導向語言來講,單體模式是建立在其類別和實例化的自然特性之上的,即使用關鍵字class定義一個類,該類可透過new關鍵字來實例化,但是需要保證每次被new實例化之後得到的都是同一個實例或者說只能透過new來調用其構造函數一次。

再來看看javascript中對於單體模式的定義:單體是一個用來劃分命名空間並將一批相關方法和屬性組織在一起的對象,如果它能夠被實例化,那麼只能被實例化一次。對比上面的定義,你會發現這裡的單體定義將其實質定義為對象,而不是傳統物件導向語言中的類,這也顯示了javascript這門語言是基於物件的。同時後面又指出,如果能夠被實例化,這說明了在javascript中單體定義應該有好幾種方式,存在一種或幾種能夠被實例化即使用new關鍵字來創建單體物件的方式,但這種方式不是javascript本身的自然特徵,因為使用new關鍵字創造出來的對象,實際上都是透過function來模擬定義其建構函式的(雖然ES6開始支援class關鍵字了,但目前還沒有被瀏覽器廣泛支援),那麼如何使用javascript的自然特徵來實現單體模式呢?

var Singleton={
  attribute1:true,
  attribute2:10,
  method1:function(){

  },
  method2:function(arg){

  }
}

這裡定義了一個物件Singleton,內部包含若干屬性和方法,將其包含在頁面中,js載入的時候就創建了這個對象,在調用時使用Singleton.method1來調用,它的實例化是隨著頁面載入js解析執行過程中完成的,我們並沒有使用new關鍵字來實例化這個對象,這也是javascript實現單體模式和傳統物件導向語言一個很大的不同。這種方式更為簡單又易於理解。但這種方式有若干缺點,一個很明顯的缺點是它並沒有提供命名空間,其他程式設計師如果在頁面中也定義了一個Singleton變量,那麼很容易改寫和混淆這個單體對象,於是針對這個問題,改寫如下:

var mySpace={};
mySpace.Singleton={
  attribute1:true,
  attribute2:10,
  method1:function(){

  },
  method2:function(arg){

  }
}


這裡首先定義了一個mySpace的命名空間,然後將單體物件Singleton掛載在這個物件的下面,這大大減少了和其他程式設計師衝突以及誤操作的可能,即使其他人在全域作用域中定義一個Singleton變量,也不會污染到這個單體對象,這實現了前面定義中所說的劃分命名空間並且將一些相關屬性和方法組織在一起的功能。

這個方法仍然存在缺點,這個單體物件的所有屬性和方法都是共有的,外部可隨時存取和修改,於是採用閉包來模擬私有屬性和方法,如下:

mySpace.Singleton=(function(){
  var privateAttribute1=false;
  var privateAttribute1=[1,2,3];
  function privateMethod1(){

  }
  function privateMethod2(){

  }

  return {
  publicAttribute1:true,
  publicAttribute2:10,
  publicMethod1:function(){
    privateAttribute1=true;
    privateMethod1();
  },
  publicMethod2:function(arg){
    privateAttribute1=[4,5,6];
    privateMethod2();
  }

  }

})();


這裡我們直接給該單體物件賦值了一個匿名自執行的函數,在該函數中使用var和function關鍵字分別來定義其私有屬性和方法,這些在函數外部(單體物件外部)是無法直接訪問的,因為函數一執行完畢,其內部作用域的空間就會被回收,這也就是能夠利用閉包來模擬私有屬性和方法的原因所在。在該函數(閉包)中,同時最終返回一個對象,這個對像中包含一些公有方法和屬性,在外部可以直接調用,同時這些公有方法由於定義在函數內部,所以可以調用其私有屬性和方法,但是外界只能透過傳回的公有方法和屬性來完成某些操作,無法直接呼叫Singleton.privateMethod1這些屬性。這就使得該單體物件既隔離了外界去直接存取其私有屬性和方法,又提供給外界一些共有屬性和方法去完成某些操作。

這種匿名函數自執行所構造的單體模式在很多js庫中被廣泛使用,但是依然存在一個問題,如果我們在載入頁面的時候並不需要用到該對象,而且該對象的當創建比較耗費開銷(如需要進行大量計算或需要多次訪問dom樹及其屬性等)時,合理的做法是需要它的時候再去創建它,而不是隨著js的解析執行直接去創建,這種概念稱為惰性載重(lazy loading),於是修改以上程式碼如下:

mySpace.Singleton=(function(){
    var uniqueInstance;
    function constructor(){
      var privateAttribute1=false;
      var privateAttribute1=[1,2,3];
      function privateMethod1(){
      }
      function privateMethod2(){
      }
      return {
        publicAttribute1:true,
        publicAttribute2:10,
        publicMethod1:function(){
          privateAttribute1=true;
          privateMethod1();
        },
        publicMethod2:function(arg){
          privateAttribute1=[4,5,6];
          privateMethod2();
        }

      }
    }

    return {
      getInstance:function(){
       if(!uniqueInstance){
         uniqueInstance=constructor();
       }
        return uniqueInstance;
      }
    }

  })();

這裡首先在匿名函數中定義了一個私有變數uniqueInstance,作為一個判斷單體物件是否被創建出來的句柄,然後將剛才所有對單體物件定義的屬性和方法都放在一個名為constructor的函數中,只有該函數呼叫了,才會創造出該單體對象,否則不會直接建立它。然後,返回一個對象,其包含一個getInstance方法,該方法是供外部調用的,調用該方法的時候首先判斷該單體對像是否存在,如果存在就直接返回它,否則調用constructor函數構造這個單體對象再返回它。最後如果我們呼叫該單體物件的某個方法,需要使用mySpace.Singleton.getInstance().publicMethod1(),這裡,只有我們這樣呼叫的時候才會建立這個單體對象,否則該單體物件是不會被自動建立的,這實際上實現了按需載入或惰性載入。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn