單例模式
的定義:一個類僅有一個實例,而且能夠在全局訪問
。
何時須要用到單例模式呢?其實單例模式在平常開發中的使用很是的普遍,例如各類浮窗、像登陸浮窗等,不管咱們點擊多少次,都是同一個浮窗,浮窗從始至終只建立了一次。這種場景就十分適合運用單例模式。閉包
咱們建立一個「最老的人」的類,很明顯,「最老的人」有且只有一個。這很符合咱們單例模式的運用場景。咱們先來看看完整代碼:函數
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」。
單例模式的定義:一個類僅有一個實例,而且能夠在全局訪問。適用場景:其實單例模式在平常開發中的使用很是的普遍,例如各類浮窗、像登陸浮窗等,不管咱們點擊多少次,都是同一個浮窗,浮窗從始至終只建立了一次。這種場景就十分適合運用單例模式。