代码清单5-3是一个很小的Web页面,展示了这3个对象的继承机制。这个页面只包含3个按钮,每个按钮创建一个类型的对象(Vehicle、SportsCar或CementTruck),并把对象传递到describe函数。describe函数负责显示各个对象的属性值,以及对象函数的返回值。注意,describe方法并不知道它描述的对象是Vehicle、SportsCar,还是CementTruck,它只是认为这个对象有适当的属性和函数,并由这个对象返回自己的值。
代码清单5-3 inheritanceViaPrototype.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>JavaScript Inheritance via Prototype</title>
<script type="text/javascript" src="inheritanceViaPrototype.js"></script>
<script type="text/javaScript">
function describe(vehicle) {
var description = "";
description = description + "Number of wheels: " + vehicle.wheelCount;
description = description + "
Curb Weight: " + vehicle.curbWeightInPounds;
description = description + "
Refueling Method: " + vehicle.refuel();
description = description + "
Main Tasks: " + vehicle.mainTasks();
alert(description);
}
function createVehicle() {
var vehicle = new Vehicle();
describe(vehicle);
}
function createSportsCar() {
var sportsCar = new SportsCar();
describe(sportsCar);
}
function createCementTruck() {
var cementTruck = new CementTruck();
describe(cementTruck);
}
</script>
</head>
<body>
<h1>Examples of JavaScript Inheritance via the Prototype Method</h1>
<br/><br/>
<button onclick="createVehicle();">Create an instance of Vehicle</button>
<br/><br/>
<button onclick="createSportsCar();">Create an instance of SportsCar</button>
<br/><br/>
<button onclick="createCementTruck();">Create an instance of CementTruck</button>
</body>
</html>
分别创建3个对象,并用describe函数描述,结果如图5-16所示。
图5-16 创建Vehicle、SportsCar和CementTruck 对象并使用describe函数分别描述的结果
5.7.2 私有属性和使用JavaScript的信息隐藏
铁杆的面向对象设计支持者会注意到,当使用prototype方法向JavaScript对象增加属性和函数时,所增加的属性和函数都是公用的,所有其他对象都能访问。对于函数来说,这通常没有问题,因为大多数函数都确实应当提供给外部客户。但是对于属性,面向对象设计的支持者就会指出,公有属性违反了信息隐藏的概念,对象的属性应当是私有的,因此外部客户不能直接访问。外部客户只能通过公用可用的函数来访问对象的私有属性。
对于JavaScript,同样有可能创建外部客户不能访问的私有属性,而只能通过对象的(公用)方法来访问,但这一点很少有人知道。Douglas Crockford[3]提出了一种在JavaScript中创建私有属性的方法。这种方法非常简单,总结如下:
l 私有属性可以在构造函数中使用var关键字定义。
l 私有属性只能由特权函数(privileged function)公用访问。特权函数就是在构造函数中使用this关键字定义的函数。外部客户可以访问特权函数,而且特权函数可以访问对象的私有属性。
下面来考虑前一个示例中的Vehicle类。假设你想让wheelCount和curbWeightIn- Pounds属性是私有的,并只能通过公用方法访问。新的Vehicle对象如代码清单5-4所示。
代码清单5-4 重写后的Vehicle对象
function Vehicle() {
var wheelCount = 4;
var curbWeightInPounds = 4000;
this.getWheelCount = function() {
return wheelCount;
}
this.setWheelCount = function(count) {
wheelCount = count;
}
this.getCurbWeightInPounds = function() {
return curbWeightInPounds;
}
this.setCurbWeightInPounds = function(weight) {
curbWeightInPounds = weight;
}
this.refuel = function() {
return "Refueling Vehicle with regular 87 octane gasoline";
}
this.mainTasks = function() {
return "Driving to work, school, and the grocery store";
}
}
注意,wheelCount和curbWeightInPounds属性都在构造函数中使用var关键字定义,这就使得这两个属性是私有属性。属性不再是公用的,如果
想通过点记法访问wheelCount属性的值,如下:
var numberOfWheels = vehicle.wheelCount;
就会返回undefined,而不是wheelCount实际的值。
由于属性现在是私有的,因此需要提供能访问这些属性的公用函数。getWheelCount、setWheelCount、getCurbWeightInPounds和
setCurbWeightInPounds函数就是作此使用的。现在Vehicle对象可以保证只能通过公用函数访问私有属性,因此满足了信息隐藏的概念。
5.7.3 JavaScript中基于类的继承
JavaScript中基于prototype的继承机制可以很好地工作,但是对于一些已经习惯于C++和Java等语言中基于类的继承机制的人来说,
JavaScript的prototype继承机制不是一种自然的编程方法。如果你不想用基于prototype的继承,而想用一种基于类的继承方法,那就继续读
下去吧。
Netscape的Bob Clary[4]也提出了一个方法,它可以使一个对象使用一个通用的脚本从另一个对象继承属性和函数。这个脚本只是将“父
”对象的属性和函数简单地复制到“子”对象。为此,我们将说明如何对脚本稍加修改,从而只是将子对象中不存在的属性和函数复制到子对
象;这样一来,子对象中的函数就能覆盖父对象的函数。在两个对象之间创建继承关系的通用函数如下:
function createInheritance(parent, child) {
var property;
for(property in parent) {
if(!child[property]) {
child[property] = parent[property];
}
}
}