在面向對象的編程中,類是不能夠直接訪問的,必須實例化後才可以訪問,但靜態屬性和方法與類本身直接聯繫,可以直接通過類來訪問。例如,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