一、概述 |
所謂單例模式,顧名思義即一個類只有一個實例。
所以,當我們創建一個實例時,就必須判斷其是否已經存在了這個實例,如果已經存在了這個實例,那么就返回這個已經存在的實例,無需再創建一個(單例模式嘛,核心就是一個類只有一個 實例);如果不存在,就創建這個實例咯。
好了,單例模式的核心思想以及創建流程大致搞清楚了,那么我們就開始看看,在Javascript的世界中,具體該怎么實現呢?
二、實戰一 |
核心思路:利用Javascript的作用域,形成閉包,從而可以創建私有變量(假設我們將這個私有變量取名為instance),然后將創建的實例賦予這個私有變量instance就ok了。每當想創建這個類的實例時,先判斷instance是否已經引用了存在的實例,如果沒有引用,即這個類沒有被創建實例,so創建一個實例,然后將其賦予給instance;如果instance已經引用,即已存在了該類的實例,so無需再創建,直接使用這個instance就ok了。
第一步:執行匿名函數,防止命名空間污染。在匿名函數中,首先定義個上述提到的私有變量instance以及一個類。這個類,我假設它有名字(name)和年齡(age)兩個屬性字段以及一個輸出他們名字的方(displayInfo)哈。
'use strict' var singletonAccepter =(function(){ //默認將instance賦予null var instance = null; //類:SupposeClass function SupposeClass( args ){ var args = args || {}; this.name = args.name || 'Monkey'; this.age = args.age || 24; }; SupposeClass.prototype = { constructor: SupposeClass, displayInfo: function(){ console.log('name: ' + this.name + ' age: ' + this.age); } }; })();
第二步:利用return + 對象字面量,將我們想,向外暴露的東東,往外拋。
如下:
return { //類的名字 name: 'SupposeClass', //創建類的實例方法 getInstance: function( args ){ //利用私有變量instance實現單例模式 if( instance === null ){ instance = new SupposeClass( args ); } return instance; } };
最后,合並第一步第二步的代碼就形成了一個單例模式啦。
如下:
'use strict' var singletonAccepter =(function(){ //默認將instance賦予null var instance = null; //類:SupposeClass function SupposeClass( args ){ var args = args || {}; this.name = args.name || 'Monkey'; this.age = args.age || 24; }; SupposeClass.prototype = { constructor: SupposeClass, displayInfo: function(){ console.log('name: ' + this.name + ' age: ' + this.age); } }; return { //類的名字 name: 'SupposeClass', //創建類的實例方法 getInstance: function( args ){ //利用私有變量instance實現單例模式 if( instance === null ){ instance = new SupposeClass( args ); } return instance; } }; })();
接下來,我們檢驗一下寫的這個單例模式。在上述代碼中,在類SupposeClass中加入console.log,如果只創建了它的一個實例,那么就只會打印一個日志哦。
修改代碼如下:
'use strict' var singletonAccepter =(function(){ var instance = null; function SupposeClass( args ){ var args = args || {}; this.name = args.name || 'Monkey'; this.age = args.age || 24; //檢驗單例模式 console.log('this is created!'); }; SupposeClass.prototype = { constructor: SupposeClass, displayInfo: function(){ console.log('name: ' + this.name + ' age: ' + this.age); } }; return { name: 'SupposeClass', getInstance: function( args ){ if( instance === null ){ instance = new SupposeClass( args ); } return instance; } }; })();
調用兩次getInstance方法,看看打印幾條記錄
singletonAccepter.getInstance();
singletonAccepter.getInstance();
執行代碼,打開chrome截圖如下:
鑒定完畢,只被實例一次。
三、實戰二 |
思路:利用屬性來判斷是否已存在實例。
什么意思?
在Javascript的世界里,類(function)不也是對象嘛,so對其賦予一個屬性instance,用來引用創建的實例,通過判斷instance是否已引用創建的實例就OK咯。
如下:
function singletonAccepter( args ){ //判斷Universe.instance是否已存在實例 if(typeof singletonAccepter.instance === 'object'){ return singletonAccepter.instance; } this.name = args.name || 'Monkey'; this.age = args.age || 24; singletonAccepter.instance = this; }; singletonAccepter.prototype = { constructor: singletonAccepter, displayInfo: function(){ console.log('name: ' + this.name + ' age: ' + this.age); } };
四、實戰三 |
在Javascript的世界里,this是引用的對象。
還記得JavaScript是怎么通過new創建對象的么?
new:
1、創建一個新的對象,這個對象的類型時object;
2、將這個對象的__proto__隱指針指向原型prototype;
3、執行構造函數,當this被提及的時候,代表新創建的對象;
4、返回新創建的對象。
注:倘若在最后return了,那么return的是基本類型,例如3,則無效;否則是引用類型,則返回這個引用類型。
注意第3點了么?
當new后,this代表新創建的對象。so,我們可以利用閉包,在類中聲明一個變量instance來引用創建的實例。然后再重寫類,就OK啦。
如下:
function singletonAccepter( args ){ var instance = null; var args = args || {}; this.name = args.name || 'Monkey'; this.age = args.age || 24; //將instance引用創建的實例this instance = this; //重寫構造函數 singletonAccepter = function(){ return instance; } }; singletonAccepter.prototype = { constructor: singletonAccepter, displayInfo: function(){ console.log('name: ' + this.name + ' age: ' + this.age); } };