在面向对象的编程中,类是不能够直接访问的,必须实例化后才可以访问,但静态属性和方法与类本身直接联系,可以直接通过类来访问。例如,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