讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議100:謹慎使用類的靜態成員 >

建議100:謹慎使用類的靜態成員

在面向對象的編程中,類是不能夠直接訪問的,必須實例化後才可以訪問,但靜態屬性和方法與類本身直接聯繫,可以直接通過類來訪問。例如,JavaScript核心對像中的Math和Global都是靜態對象,不需要實例化就可以直接訪問。

類的靜態成員包括私有和公共兩種類型,不管是公共成員還是私有成員,它們在系統中只有一份副本,不會被分成多份傳遞給不同的對象,而是通過函數指針進行引用,這與閉包截然不同。下面示例為類型定義一個私有的靜態成員。


var F=(function{

var_a=1;//私有變量

this.a=_a;//公共屬性

this.get1=function{//公共方法

return_a;

};

this.set1=function(x){//公共方法

_a=x;

};

return function{//構造函數類

this.get2=function{//提供訪問私有變量的接口

return_a;

};

this.set2=function(x){//提供修改私有變量的接口

_a=x;

};

}

});

//定義類的靜態公共方法和屬性

F.get3=function{

return get1;

};

F.set3=function(x){

set1(x);

}


與一般類的創建方法一樣,這裡的私有成員和特權成員仍然被聲明在構造器中,並借助var和this關鍵字來實現。這裡的構造器卻由原來的普通函數變成了一個內嵌函數,並且作為外層函數的返回值賦值給了變量F,這就創建了一個閉包。在這個閉包中,還可以聲明靜態私有成員,例如:


var F=(function{

function set5(x){//靜態私有方法

_a=x;

}

function get5{//靜態私有方法

return_a;

}

});


這些靜態私有成員可以在構造器內部訪問,這意味著所有私有函數和特權函數都能訪問它們。與其他方法相比,靜態方法有一個優點,那就是在內存中僅存放一份。那些被聲明在構造器之外的公共靜態方法,以及下文中將要提到的F類原型屬性都不能訪問在構造器中定義的任何私有屬性,因此它們不是特權成員。

定義在構造器中的私有方法能夠調用其中的靜態私有方法,反之則不然。要判斷一個私有方法是否應該被設計為靜態方法,可以看它是否需要訪問任何實例數據。如果它不需要,那麼將其設計為靜態方法會更有效率,因為它只被創建一份。

定義類的靜態公共方法和屬性一般在類的外面進行,這種外掛定義的方式在前面的示例中也曾經介紹過。這種外掛的靜態方法和屬性可以直接訪問,這實際上相當於把構造器作為命名空間來使用。同時,由於它們仍然屬於構造器結構的一部分,因此在這些靜態方法和屬性中可以訪問閉包中的私有成員。


alert(F.get3);//直接訪問類F的靜態方法get3,返回1

F.set3(2);//修改私有變量的值

alert(F.get3);//2,說明修改成功


注意,類F是返回的內層函數,該值是一個構造函數,它無法訪問外層函數的公共方法get1和set1,但能夠訪問返回構造函數體內的公共方法get2和set2。例如:


var a=new F//實例化類F

alert(a.get2);//調用類F的公共方法get2,返回1

a.set2(2);//調用類F的公共方法set2,修改私有變量_a

alert(a.get2);//調用類F的公共方法get2,返回2


但下面的用法都是錯誤的,因為級別比較低的F類(F是返回的匿名構造函數)無權訪問閉包體內的變量、屬性和方法(不管是私有的還是公共的)。


var a=new F

alert(a.get1);

a.set1(2);

alert(a.get1);


閉包體內的所有對象都可以訪問閉包體內的私有或公共變量、屬性和方法。由於類F是閉包體內返回的構造函數,因此根據作用域鏈,它們可以向上訪問閉包所有成員。


F.prototype={//類F的原型對像

get4:function{//原型方法get4

return get1;//訪問閉包內數據

},

set4:function(x){//原型方法set4

set1(x);//訪問閉包內數據

}

};

var a=new F;//實例化類F

alert(a.get4);//1