深入理解JavaScript模擬私有成員
一般的面向對象語言C++或JAVA,對象都是有私有成員的。js中沒有類的改變,同樣也沒有對象的私有成員這個概念。但是可以通過某些特殊寫法,模擬出私有成員。
1、特權模式:
(1)在構造函數內部聲明的變量、子函數以及參數,全部都是函數私有的,可以看作私有成員。給this指針添加的閉包,全部都是公有成員。
所以下面例子:參數a/b、變量_value、函數add是私有的,外面無法訪問,故c1.add會報錯
setValue和getValue是公有的,c1可以訪問
function MyClass(a,b){
//private
var _value=0;
function add(){
_value =a+b;
}
//process
add();
//public
this.getValue=function(){
return _value;
}
this.setValue=function(a1,b1){
a=a1;
b=b1;
add();
}
}
var c1=new MyClass(1,2);
var c2=new MyClass(1,2);
c1.setValue(3,4);
alert(c1.getValue()); //7
alert(c2.getValue());//3
alert(c1.add());//c1中無add方法,報錯c1.add is not a function(…)
(2)這種模擬方法的優點是代碼清晰明了,缺點是每個對象的成員函數,不論是私有還是公有,都是自己獨有的。即便成員函數的功能相同,但他們卻是存放在不同位置的不同函數。
(3)這種模擬方法只模仿了一個外形。OOP語言中,每個對象的成員函數,在內存中只體現為一個單元,我們違背了這一原則。
2、私有作用域模式:
(1)既然模擬出了塊級作用域,可以考慮把塊級作用域拿來,模擬一下私有成員。
(function(){
//private
var _value=0, _a=0, _b=0;
function add(){
_value=_a+_b;
}
//construct
MyClass=function(a,b){
_a=a;
_b=b;
add();
}
//public
MyClass.prototype.getValue=function(){
return _value;
}
MyClass.prototype.setValue=function(a1,b1){
_a=a1;
_b=b1;
add();
}
})();
var c1=new MyClass(1,2);
var c2=new MyClass(1,2);
c1.setValue(3,4);
alert(c1.getValue());//7
alert(c2.getValue());//7
alert(c1.add());//報錯
(2)上面例子我們使用了塊級作用域,_value、_a、_b、add都是匿名函數的私有變量;MyClass前面沒有var,所有MyClass是全局變量,可以在塊級作用域外部使用;getValue和setValue是通過原型模式為MyClass添加的公共方法。
(3)這樣寫,MyClass所有實例的公共方法不再單獨創建,而是共享使用;而私有方法,則是共享作用域鏈中的add,也不是獨立創建的。針對成員方法而言,已經與C++和java非常接近了。
(4)但這樣寫的缺點也是顯而易見的,就是私有數據成員_value、_a、_b在各實例之間也是共享的。這些成員,相當於static成員。
3、綜合模式
(function(){
//private static
var _count=0;
function add(a,b){
return a+b;
}
//construct
MyClass=function(a,b){
//private
var _data={ value:0 }
//process
_data.value=add(a,b);
_count++;
//public
this.data=function(){return _data;}
}
//public
MyClass.prototype.getValue=function(){
return this.data().value;
}
MyClass.prototype.setValue=function(a,b){
this.data().value=add(a,b);
}
MyClass.prototype.getCount=function(){
return _count;
}
})();
var c1=new MyClass(1,2);
var c2=new MyClass(1,2);
c1.setValue(3,4);
alert(c1.getValue());//7
alert(c2.getValue());//3
alert(c1.getCount());//2
alert(c2.getCount());//2
(1)為了解決私有作用域模式中私有數據成員共享的問題,必須將私有數據成員寫在MyClass內。但這樣,公有成員函數就不能使用他們了。所以還是要使用一個特權函數,提供私有數據成員的接口,這個是無法避免的,因此只能讓代價盡量的小,比如對私有數據成員做一個打包。
(2)私有方法是內存共享的,比如add函數。然而實際上,私有方法只要共享內存,就是private static狀態,而private static狀態方法,如果想使用非形參變量時,也只能使用private static的變量,比如_count。想使用實例的私有數據成員,只能通過形參傳入。
(3)公有方法是內存共享的,是通過原型鏈實現的。公有方法使用私有數據成員,就必須得到私有數據成員的接口。我們做的this.data就是接口。
(4)整個模式中,閉包個數只有一個,就是this.data,這個閉包是無法避免的。
深入理解JavaScript模擬私有成員