《設計模式》 - 1. 單例模式( Singleton )

Cleve_baby發表於2018-07-06

Javascript設計模式 - 原文連結

單例模式 :

語言 : JavaScript

定義 :

單例模式有兩個要點,保證一個類只有一個例項,並提供訪問該例項的全域性訪問點。

說明 :

這篇文章通過一個簡單的建立Mask的需求 , 一步步優化程式碼中 , 循序漸進 , 通俗易懂地講解了單例的產生以及運用 , 加上了一些個人理解 .

假定需求 : 在點選按鈕需要彈出一個遮罩層的時 . (例如 web.qq.com點選登入的時候) 
這裡寫圖片描述


第一次處理 :

建立 div :

var createMask = function(){

   return document.body.appendChild(document.createElement(div));

}

給按鈕新增點選回撥方法 :

$('button').click(function(){

   var mask  = createMask();
   mask.show();

})

現在的處理雖然可以解決需求, 但是每次點選按鈕的時候都建立出一個新的div, 這很顯然不是很好的解決辦法


進行改進 :

建立一個mask變數, 只建立一次div, 每次需要顯示的時候, 只需要呼叫show();方法即可

var mask = document.body.appendChild(document.createElement('div'));

$('button').click(function(){

   mask.show();

})

這樣處理雖然表面上可以減少建立 和 移除 div 的次數, 可是如果我們從始至終都不需要使用這個div 遮罩呢? 那不是白白浪費了這個div, 對dom節點的任何操作都應該非常吝嗇, 顯然我們需要再次改進.


再次改進 :

我們藉助一個變數來判斷是否建立過這個div

var mask;

var createMask = function(){
    if (mask)
    {
        return mask;
    }
    else
    {
        mask = document,body.appendChild(document.createElement(div));
        return mask;
    }
}

這樣處理看起來就好很多了, 完成了一個基本的單例, 既不會有多餘的建立, 也不會白白建立一個不需要的div.

但是, 如果仔細研究這個函式, 還是能發現其中的問題, 函式體內改變了外界變數mask的引用, 在多人協作的專案中, createMask是個不安全的函式. 另一方面, mask這個全域性變數並不是非需不可.


第三次改進 :

var createMask = function(){
  var mask;
  return function()
  {
       return mask || (mask = document.body.appendChild(document.createElement('div')))
  }
}()

用了一個閉包把變數mask包起來, 使得這個函式是一個封閉的函式.


原文中提到, 在js中函式是第一型, 意味著函式也可以當引數傳遞. 
它只能用於建立遮罩層. 假如我又需要寫一個函式, 用來建立一個唯一的xhr物件呢? 能不能找到一個通用的singleton包裝器.

單例模式最終程式碼 :

var singleton = function(fn){
    var result;
    return function()
    {
        return result || ( result = fn .apply( this, arguments ) );
    }
}

var createMask = singleton(function(){

return document.body.appendChild(document.createElement('div'));

 })

用一個變數來儲存第一次的返回值, 如果它已經被賦值過, 那麼在以後的呼叫中優先返回該變數. 而真正建立遮罩層的程式碼是通過回撥函式的方式傳人到singleton包裝器中的. 這種方式其實叫橋接模式. 關於橋接模式, 放在後面一點點來說. 
然而singleton函式也不是完美的, 它始終還是需要一個變數result來寄存div的引用. 遺憾的是js的函式式特性還不足以完全的消除宣告和語句.

相關文章