静态成员(Static Members)
静态的属性和方法就是那些在所有实例间都相同的属性和方法。在基于类的语言中,静态成员通常使用特殊的语法创建,然后就可以被使用,它们就像是像类的自身成员一样。
比如:一些MathUtils一个静态方法max()可能这样被调用 MathUtils.max(3, 5),这是一个public静态成员的例子,可以不必创建对象的实例就可以使用。
也可以有private的静态属性,对类的使用者是不可见的但仍能在这个类的所有实例间共享。让我看看在JavaScript中如何实现public和private的静态成员。
公共的静态成员(Public Static Members)
在JavaScript中没有特殊的语法表示静态成员。但你也有一样的语法,就像“类”语言一样,通过给构造函数添加属性。
这样可行,是因为构造函数和其它函数一样是对象并且它们可以拥有属性。
下面这个例子定义了一个构造函数Gadget,拥有一个静态方法isShiny()和一个普通实例方法setPrice(),isShiny()是一个静态方法因为它不需要一个具体的gadget对象就可以工作(就像你不需要一个特殊的gadget得出所有的gagdet是shiny)。setPrice()在另一方面而言,需要一个对象,因为gadget可以有不同的价格。
// constructor
var Gadget = function () {};
// a static method
Gadget.isShiny = function () {
return "you bet";
};
// a normal method added to the prototype
Gadget.prototype.setPrice = function (price) {
this.price = price;
};
让我们来调用这些方法,静态方法
isShiny()可以直接在构造函数上被调用,然而普通方法则需要一个实例:
// calling a static method
Gadget.isShiny(); // "you bet"
// creating an instance and calling a method
var iphone = new Gadget();
iphone.setPrice(500);
试图静态调用实例方法是不能工作的,使用一个iphone对象调用静态方法也是不可以的:
typeof Gadget.setPrice; // "undefined"
typeof iphone.isShiny; // "undefined"
有时候静态方法也可以通过实例调用可能是非常方便的,这个非常容易实现,通过给原型添加一个新方法,作为一个门面( façade)指向原来的静态方法:
Gadget.prototype.isShiny = Gadget.isShiny;
iphone.isShiny(); // "you bet"
在这种情况下,你需要格外小心如果你在静态方法中使用this,当你这样做Gadget.isShiny()并且在isShiny()方法中使用this,this将会指向Gadget构造函数,
如果你这样做iphone.isShiny(),那么this指向iphone
我们最后一个例子展示如何可以拥有和上面一样的方法,可以静态的被调用,也可以非静态的被调用并且表现有一些不同,这依赖于被调用的模式。
这里instance of 可以帮助我们判断方法是如何被调用的:
// constructor
var Gadget = function (price) {
this.price = price;
};
// a static method
Gadget.isShiny = function () {
// this always works
var msg = "you bet";
if (this instanceof Gadget) {
// this only works if called non-statically
msg += ", it costs $" + this.price + '!';
}
return msg;
};
// a normal method added to the prototype
Gadget.prototype.isShiny = function () {
return Gadget.isShiny.call(this);
};
测试一个静态方法调用:
Gadget.isShiny(); // "you bet"
测试一个实例,非静态调用:
var a = new Gadget('499.99');
a.isShiny(); // "you bet, it costs $499.99!"
私有的静态属性(Private Static Members)
到目前为止讨论的都是public静态方法,现在让我们看一下如何能实现private的静态成员。
private的静态成员,意味着这些成员:
- 在相同构造函数创建的所有对象间共享
- 在构造函数外不可访问
让我们看一个例子,counter是一个在Gadget构造函数中的private静态成员,我们已经讨论过私有属性,所以这部分仍然是一样的——你需要一个函数作为一个闭包并且包裹住所有私有属性。
那么我们有一个相同的立即执行的包裹函数,并且返回一个新的函数。返回的函数被赋值给变量Gadget,并且作为一个构造函数:
var Gadget = (function () {
// static variable/property
var counter = 0;
// returning the new implementation
// of the constructor
return function () {
console.log(counter += 1);
};
}()); // execute immediately
新的Gadget构造函数简单的增加并且打印私有的counter。使用一些实例测试,你会发现counter的确在所有的实例间共享、
var g1 = new Gadget(); // logs 1
var g2 = new Gadget(); // logs 2
var g3 = new Gadget(); // logs 3
因为我们每个对象都将counter加1,这个静态属性成为Gadget构造函数创建的每个对象一个ID(独一无二的标识)。
这个独一无二标识是可能是有用的,那么为什么不用特权方法(privileged method)暴露它呢?
下面的例子在前一个例子的基础之上构建,添加了特权方法getLastId() 去访问私有的静态属性:
// constructor
var Gadget = (function () {
// static variable/property
var counter = 0,
NewGadget;
// this will become the
// new constructor implementation
NewGadget = function () {
counter += 1;
};
// a privileged method
NewGadget.prototype.getLastId = function () {
return counter;
};
// overwrite the constructor
return NewGadget;
}()); // execute immediately
测试新的实现:
var iphone = new Gadget();
iphone.getLastId(); // 1
var ipod = new Gadget();
ipod.getLastId(); // 2
var ipad = new Gadget();
ipad.getLastId(); // 3
静态属性包括私有和公共的(private and public))是十分方便的,它们可以包括方法和数据(data),不是实例指定的(instance-specific)的,每个实例都不需要重新创建。
当我们谈到单例模式时,你可以看到一个例子实现,使用静态属性实现一个单例构造函数。