javascript 建立私人成員和靜態私人成員
私人方法和屬性
在javascript中,因為沒有類的概念,所以只能通過建構函式來類比類,假設現在需要寫一個手機類,這個手機需要傳入一張電話號碼,然後能打電話。最簡單的方式是
var Phone= function(num){ //手機號 this.num= num; //打電話 this.call = function(phone){ console.log(this.num + 正在呼叫 + phone.num); }}var p1= new Phone(13012341234);
為了資料的正確性,我們可能還會在給num賦值的時候檢查其合法性,比如
var Phone= function(num){ if(!this.checkNum(num)){ throw new Error(手機號非法!); } //手機號 this.num= num; //打電話 this.call = function(phone){ console.log(this.num + 正在呼叫 + phone.num); }}//驗證手機號合法性Phone.prototype.checkNum = function(num){ if(/^1[358]d{9}$/.test(num)){ return true; }else{ return false; }}var p1 = new Phone(13312341234);var p2 = new Phone(15812341234);p1.call(p2); //13312341234正在呼叫15812341234
這樣情況會好很多,但是雖然在建立對象的時候對num進行了控制,但當這段代碼給其他程式員用的時候,他可能會直接給手機賦值,比如
p1.num = abcdefg;p1.call(p2);
即使能在建構函式中對資料的完整性進行檢驗,但我們還是無法控制屬性被任意修改,對於這種情況,我們可以利用閉包來將num變為私人變數,外界想訪問num只能通過其get和set方法。
var Phone= function(num){ //私人變數 var _num; //特權方法 this.setNum = function(num){ if(!this.checkNum(num)){ throw new Error(手機號非法!); } _num = num; }; this.getNum = function(){ return _num; }; this.call = function(phone){ console.log(_num + 正在呼叫 + phone.getNum()); }; //初始化 this.setNum(num);}Phone.prototype.checkNum = function(num){ if(/^1[358]d{9}$/.test(num)){ return true; }else{ return false; }}var p1 = new Phone(13312341234);var p2 = new Phone(15812341234);p1.call(p2); //13312341234正在呼叫15812341234
如num一樣的屬性或方法是用var關鍵字聲明的,而不是this,這就代表其只能在函數內部使用,因此形成了一個私人屬性,私人方法也是類似,在建構函式裡直接聲明一個函數即是私人方法。私人方法不能被外部直接存取,但又能通過像setNum和getNum這樣的函數通過閉包特點訪問,這種能訪問私人屬性的公有方法稱為特權方法。
然而這種方式建立對象也有弊端,call函數調用了私人屬性num,所以它只能放在建構函式裡面,而特權方法太多往往會佔用過多記憶體,因為每產生一個新的對象執行個體都將為每個私人方法和特權方法產生一個副本,這也比最開始的方式耗費更多記憶體。
靜態方法和屬性
靜態成員是和類有關的,而不是和執行個體相關聯,也就是說靜態成員直接通過類訪問,所有執行個體共用一份靜態成員,實現共有靜態方法很簡單,只需通過將其賦值給建構函式即可,如Phone.count = 5;但私人靜態成員是不能被外部直接存取的,這就又需要用到閉包:
var Phone= (function(){ //靜態私人屬性 var count = 0; return function(num){ //私人變數 var _num; //特權方法 this.setNum = function(num){ if(!this.checkNum(num)){ throw new Error(手機號非法!); } _num = num; }; this.getNum = function(){ return _num; }; this.call = function(phone){ console.log(_num + 正在呼叫 + phone.getNum()); }; //超過50個手機不予建立 count++; if(count > 50){ throw new Error(只能建立50個手機!); } //初始化 this.setNum(num); }})()Phone.prototype.checkNum = function(num){ if(/^1[358]d{9}$/.test(num)){ return true; }else{ return false; }}var p1 = new Phone(13312341234);var p2 = new Phone(15812341234);p1.call(p2); //13312341234正在呼叫15812341234
和上面建立的方式大體相似,但這裡的建構函式變成了一個內嵌函數,通過外部函數的立即執行被返回,這也就使得外部函數的count能被建構函式訪問到,因為外部函數只被載入一次,所以count也只有一個,當建立50個執行個體的時候,count被賦為50,若繼續建立則會報錯。