js設計模式——單例模式(讀書JavaScript設計模式與開發實戰筆記01)

單例模式

單例模式:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

	// 首先創建一個初始化的構造函數
    var CreateDiv = function (html) {
      this.html = html;
      this.init();
    }
    CreateDiv.prototype.init = function () {
      var div = document.createElement('div');
      div.innerHTML = this.html;
      document.body.appendChild(div);
    }
    // 依據單一職責原則 創建代理來實現單例
    var ProxySingletonCreateDiv = (function () {
      // 需要返回CreateDiv
      var instance;
      return function (html) {
        if (!instance) {
          instance = new CreateDiv(html);
        }
        return instance;
      }
    })();
    var a = ProxySingletonCreateDiv('viven');
    var b = ProxySingletonCreateDiv('kevin');
    console.log(a===b); // true

惰性單例模式:只有在需要的時候纔會創建對象的實例,
例如一個登陸框,通常的解決方案是創建好一個登陸窗口,隱藏,等需要的時候點擊。

	<!DOCTYPE html>
	<html lang="en">
	<head>
	  <meta charset="UTF-8">
	  <meta name="viewport" content="width=device-width, initial-scale=1.0">
	  <meta http-equiv="X-UA-Compatible" content="ie=edge">
	  <title>惰性單例模式</title>
	</head>
	<body>
	  <button id="loginBtn">登錄</button>
	  <script>
	  var loginLayer = (function () {
	    var div = document.createElement('div');
	    div.innerHTML = '懸浮登錄框';
	    div.style.display = 'none';
	    document.body.appendChild(div);
	    return div;
	  })();
	  document.getElementById('loginBtn').onclick = function () {
	    loginLayer.style.display = 'block';
	  };
	  </script>
	</body>
	</html>

但是這樣有一個問題,就是浪費了不必要的DOM節點,因爲不一定會用的上,我們的要求是在需要的時候,創建這個對象。
我們可以改造一下,就是只有在點擊的時候才創建。

	<!DOCTYPE html>
	<html lang="en">
	<head>
	  <meta charset="UTF-8">
	  <meta name="viewport" content="width=device-width, initial-scale=1.0">
	  <meta http-equiv="X-UA-Compatible" content="ie=edge">
	  <title>惰性單例模式</title>
	</head>
	<body>
	  <button id="loginBtn">登錄</button>
	  <script>
	  var createLoginLayer = function () {
	    var div = document.createElement('div');
	    div.innerHTML = '懸浮登錄框';
	    div.style.display = 'none';
	    document.body.appendChild(div);
	    return div;
	  };
	  document.getElementById('loginBtn').onclick = function () {
	    var loginLayer = createLoginLayer();
	    loginLayer.style.display = 'block';
	  };
	  </script>
	</body>
	</html>

這樣就只有在點擊的才創建,但是出現了每點擊一次就出現一個登陸框的BUG,這顯然不符合我們的需求,我們可以在創建的時候給它一個變量,當登錄框不存在的時候。則創建,當存在則不創建。

	<!DOCTYPE html>
	<html lang="en">
	<head>
	  <meta charset="UTF-8">
	  <meta name="viewport" content="width=device-width, initial-scale=1.0">
	  <meta http-equiv="X-UA-Compatible" content="ie=edge">
	  <title>惰性單例模式</title>
	</head>
	<body>
	  <button id="loginBtn">登錄</button>
	  <script>
	  var createLoginLayer = function () {
	    var div;
	    return function () {
	      if (!div) {
	        div = document.createElement('div');
	        div.innerHTML = '懸浮登錄框';
	        div.style.display = 'none';
	        document.body.appendChild(div);
	      }
	      return div;
	    };
	  }();
	  document.getElementById('loginBtn').onclick = function () {
	    var loginLayer = createLoginLayer();
	    loginLayer.style.display = 'block';
	  };
	  </script>
	</body>
	</html>

這樣就能解決多次創建的問題了,至於這裏爲什麼在createLoginLayer函數要用閉包,那是因爲我們要保持對div變量的引用,不然每次調用的時候都會重新初始化div,達不到單例的效果。
有的同學說直接把div變量放外面 行不行,當然行,但是我們學javascript都知道,要儘量減少全局變量的使用。

	<!DOCTYPE html>
	<html lang="en">
	<head>
	  <meta charset="UTF-8">
	  <meta name="viewport" content="width=device-width, initial-scale=1.0">
	  <meta http-equiv="X-UA-Compatible" content="ie=edge">
	  <title>惰性單例模式</title>
	</head>
	<body>
	  <button id="loginBtn">登錄</button>
	  <script>
	  var div;
	  var createLoginLayer = function () {
	    if (!div) {
	      div = document.createElement('div');
	      div.innerHTML = '懸浮登錄框';
	      div.style.display = 'none';
	      document.body.appendChild(div);
	    }
	    return div;
	  };
	  document.getElementById('loginBtn').onclick = function () {
	    var loginLayer = createLoginLayer();
	    loginLayer.style.display = 'block';
	  };
	  </script>
	</body>
	</html>

上面雖然能解決問題,但是強烈不推薦。

雖然我們通過閉包解決的全局變量的問題,但是上面的寫法還是違背了單一職責原則,我們需要解耦。
依據上面的代碼,我們不難發現,其實包括了兩個功能,1、創建一個窗口。2、檢查是否創建。

// 檢查是否創建
var obj;
if (!obj) {
	obj = xxx;
}
return obj;

var obj;
return obj||(obj=xxx)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>惰性單例模式</title>
</head>
<body>
  <button id="loginBtn">登錄</button>
  <script>
  	// 這裏檢查是否創建
    var getSingle = function (fn) {
      var result;
      return function () {
        return result || (result = fn.apply(this, arguments));
      }
    }
    // 這個方法是創建div
    var createLoginLayer = function(){
      var div = document.createElement( 'div' );
      div.innerHTML = '我是登錄浮窗';
      div.style.display = 'none';
      document.body.appendChild( div );
      return div;
    };
    var createSingleLoginLayer = getSingle(createLoginLayer);
    document.getElementById( 'loginBtn' ).onclick = function(){
      var loginLayer = createSingleLoginLayer();
      loginLayer.style.display = 'block';
    };
  </script>
</body>
</html>

以上就是單例模式,單例模式其實包含兩個內容,第一就是判斷是否存在,第二就創建內容。兩個內容結合就是單例模式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章