JavaScript設計模式系列二:單例模式

單例模式

什麼是單例模式?

單例模式的定義:一個類僅有一個實例,而且能夠在全局訪問
何時須要用到單例模式呢?其實單例模式在平常開發中的使用很是的普遍,例如各類浮窗、像登陸浮窗等,不管咱們點擊多少次,都是同一個浮窗,浮窗從始至終只建立了一次。這種場景就十分適合運用單例模式。閉包


代碼實現

咱們建立一個「最老的人」的類,很明顯,「最老的人」有且只有一個。這很符合咱們單例模式的運用場景。咱們先來看看完整代碼:函數

var oldestMan = function (name) {
  this.name = name;
}

oldestMan.prototype.getName = function () {
  console.log(this.name);
}
//引入一個代理函數和閉包的概念
var createOldestMan = (function () {
  var instance;
  return function (name) {
      if (!instance) {
         instance = new oldestMan(name);
      }
      return instance;
  }
})();

var personA = createOldestMan("holz");
var personB = createOldestMan("Amy");
personA.getName();  //  holz
personB.getName();  //  holz

咱們能夠在控制檯上看到即便調用了兩次createOldestMan而且賦了不同的值,但兩次getName()輸出的都是第一次的「holz」。這就是單例模式。this

代碼看不太懂?不要緊,如今給你們一一講解。
首先咱們建立了一個oldestMan類,建立了一個name屬性。而後咱們經過 prototype 給它添加一個getName()方法用來獲取oldestMan的名字,相信到這裏你們都是懂的,而後下面一段代碼就是重點了,也比較難理解。咱們打這段代碼單獨拿出來將一下。prototype

//引入一個代理函數和閉包的概念
var createOldestMan = (function () {
  var instance;
  return function (name) {
      if (!instance) {
         instance = new oldestMan(name);
      }
      return instance;
  }
})();

首先,咱們不用管什麼是代理函數,之因此叫它代理函數是由於它輔助咱們實現單例模式的效果,這段函數第一個關鍵點是 createOldestMan() 是一個當即執行函數。當即函數在聲明的時候就會當即執行,也就是在聲明createOldestMan的時候這個函數就會執行,它會聲明一個instance 變量,而後返回一個函數給createOldestMan。createOldestMan就至關於:代理

var createOldestMan = function (name) {
      if (!instance) {
         instance = new oldestMan(name);
      }
      return instance;
  }

第二個關鍵點是:這裏利用了 閉包 的概念。code

閉包是什麼呢?我只須要記住當函數在定義時的語法做用域以外被調用,卻還能訪問定義時的語法做用域時,就是產生了閉包。

咱們來看一下咱們的代碼,函數先定義了一個instance,而後再返回一個function(name),這個function(name)裏面用到了instance變量。在正常狀況下,在當即執行函數執行以後,instance變量就會被JavaScript的垃圾回收機制回收,可是由於function(name)被返回到了外部,而function(name)隨時會被調用,隨時會訪問到instance變量,因此instance變量被保留在了內存中。這就產生了閉包。也就是說,function(name)被賦值給了外部的createOldestMan,在外部的語法做用域中執行,但還能夠訪問到定義時內部的語法做用域中的instance。ip

因此在 當即執行函數閉包 的做用下,instance只被申請了一次,也就是隻有一個instance。也就是說,咱們不管執行多少次createOldestMan("..."),instance只會是第一次的那個值。因此咱們就能夠判斷instance是否已經被實例化了,來給instance賦值,若是instance已經被實例化,就返回instance。這就達到了一個類只有一個實例的效果。內存

通用的單例模式

我還能夠改造一下代碼,由於在開發中,咱們可能不只只有一個單例,因此咱們應該讓代碼可以變得各個單例通用。咱們應該在哪裏改呢?沒錯,改代理函數。咱們只須要把代理函數中的oldestMan()提取出來,改成以參數的形式傳值,不侷限於oldestMan()。作用域

var singleObj;
var createSingleton = function (fn) {
  return function (text) {
    if (!singleObj) {
      singleObj = new fn (text);
    }
    return singleObj;
  }
}

這樣咱們就能夠把單例做爲參數傳進去,用它實現不一樣的單例了。
完整代碼是這樣的:開發

var oldestMan = function (name) {
  this.name = name;
}

oldestMan.prototype.getName = function () {
  console.log(this.name);
}

//一個通用的代理函數
var singleObj;
var createSingleton = function (fn) {
  return function (text) {
    if (!singleObj) {
    singleObj = new fn (text);
    }
    return singleObj;
  }
}

var person_1 = createSingleton(oldestMan)("holz");
var person_2 = createSingleton(oldestMan)("tom");
person_1.getName(); //holz
person_2.getName(); //holz

一樣,即便再次調用createSingleton並傳入不一樣的值,輸出的依舊是第一次的「holz」。


總結

單例模式的定義:一個類僅有一個實例,而且能夠在全局訪問。適用場景:其實單例模式在平常開發中的使用很是的普遍,例如各類浮窗、像登陸浮窗等,不管咱們點擊多少次,都是同一個浮窗,浮窗從始至終只建立了一次。這種場景就十分適合運用單例模式。

相關文章
相關標籤/搜索