我们假设本书的读者对JavaScript至少有基本的实践知识。如果要提供JavaScript的全面教程,这本身就需要一本完整的书才能讲清楚,所
以在这里我们不打算详细介绍这种语言。相反,本节只是讨论JavaScript的一些可能鲜为人知的高级特性,并说明如何在你的Ajax开发中结合
使用这些特性。
我们先来简单地谈谈JavaScript的历史,以便你了解它原来是什么样子,又是怎么发展到今天的。Netscape的Brendan Eich于1995年开发
了JavaScript。他的任务本来是开发一种方法,使得创建和维护Web网站的非专业Web设计人员能够更容易地使用Java applet。Eich认为,适当
的选择是开发一种不需要编译器的弱类型语言。
最初,Eich开发的这个语言有过很多名字,不过,后来由于Java在市场上大获成功,因此借着这股东风,最后命名为JavaScript。
JavaScript很快成为Web上最流行的脚本语言,这要归功于它的低门槛,另外还因为它能够把JavaScript脚本从一个页面复制到另一个页面。在
JavaScript和Navigator DOM的早期修订版本基础上,产生了DOM Level 0标准,该标准将表单元素和图像定义为DOM元素的子元素。
Microsoft迎头赶上,创建了自己的脚本语言VBScript。VBScript在功能上与JavaScript类似,但采用了类Visual Basic语法,而且只能用
于IE。Microsoft还提供了JavaScript的一个实现JScript(现在已由ECMA标准化并称为ECMAScript)。尽管不同JavaScript的语法几乎是一样
的,但不同浏览器上DOM实现却大相径庭,以至于几乎不可能创建跨浏览器的脚本。使用“最小公分母”方法得到的脚本通常只能完成最简单的
任务。
到了1998年,Netscape开放了其浏览器的源代码,决定开始重写浏览器,并把重点放在遵循W3C标准上。那时,IE 5是W3C DOM和ECMAScript的最佳实现。开源版本的Netscape以Mozilla为名于2002年问世。由此开始,浏览器领域形成了一个趋势:越来越多的浏览器开始努力遵循W3C和ECMA维护的Web标准。如今,现代浏览器(如Firefox、Mozilla、Opera、Konqueror和Safari)都严格遵循Web标准,这就大大简化了编写跨浏览器的HTML和JavaScript等的任务。IE 6与1998年的IE 5并没有太大差别,它严格禁止了最不合标准的行为。
5.7.1 通过prototype属性建立面向对象的JavaScript
JavaScript通过一种链接机制来支持继承,而不是通过完全面向对象语言(如Java)所支持的基于类的继承模型。每个JavaScript对象都有一个内置的属性,名为prototype。prototype属性保存着对另一个JavaScript对象的引用,这个对象作为当前对象的父对象。
当通过点记法引用对象的一个函数或属性时,倘若对象上没有这个函数或属性,此时就会使用对象的prototype属性。当出现这种情况时,将检查对象prototype属性所引用的对象,查看是否有所请求的属性或函数。如果prototype属性引用的对象也没有所需的函数或属性,则会进一步检查这个对象(prototype属性引用的对象)的prototype属性,依次沿着链向上查找,直到找到所请求的函数或属性,或者到达链尾,如果已经到达链尾还没有找到,则返回undefined。从这个意义上讲,这种继承结构更应是一种“has a”关系,而不是“is a”关系。
如果你习惯于基于类的继承机制,那么可能要花一些时间来熟悉这种prototype机制。prototype机制是动态的,可以根据需要在运行时配置,而无需重新编译。你可以只在需要时才向对象增加属性和函数,而且能动态地把单独的函数合并在一起,来创建动态、全能的对象。对prototype机制的这种高度动态性可谓褒贬不一,因为这种机制学习和应用起来很不容易,但是一旦正确地加以应用,这种机制则相当强大而且非常健壮。
这种动态性与基于类的继承机制中的多态概念异曲同工。两个对象可以有相同的属性和函数,但是函数方法(实现)可以完全不同,而且属性
可以支持完全不同的数据类型。这种多态性使得JavaScript对象能够由其他脚本和函数以统一的方式处理。
图5-15显示了实际的prototype继承机制。这个脚本定义了3类对象:Vehicle、Sports-
Car和CementTruck。Vehicle是基类,另外两个类由此继承。Vehicle定义了两个属性:wheelCount和curbWeightInPounds,分别表示
Vehicle的车轮数和总重量。JavaScript不支持抽象类的概念(抽象类不能实例化,只能由其他类扩展),因此,对于Vehicle基类,
wheelCount默认为4,curbWeightInPounds默认为3 000。
图5-15 Vehicle、SportsCar和CementTruck对象之间的关系
注意,这个UML图展示了SportsCar和CementTruck对象覆盖了Vehicle的refuel和mainTasks函数,因为一般的Vehicle、SportsCar(赛车)
和CementTruck(水泥车)会以不同的方式完成这些任务。SportsCar与Vehicle的车轮数相同,所以SportsCar的wheelCount属性不会覆盖
Vehicle的wheelCount属性。CementTruck的车轮数和重量都超过了Vehicle,所以CementTruck的wheelCount和curbWeightInPounds属性要覆盖
Vehicle的相应属性。
代码清单5-2包含了定义这3个类的JavaScript代码。要特别注意如何在对象定义中对属性和函数附加prototype关键字,还要注意每个对象
由一个构造函数定义,构造函数与对象类型同名。
代码清单5-2 inheritanceViaPrototype.js
/* Constructor function for the Vehicle object */
function Vehicle() { }
/* Standard properties of a Vehicle */
Vehicle.prototype.wheelCount = 4;
Vehicle.prototype.curbWeightInPounds = 4000;
/* Function for refueling a Vehicle */
Vehicle.prototype.refuel = function() {
return "Refueling Vehicle with regular 87 octane gasoline";
}
/* Function for performing the main tasks of a Vehicle */
Vehicle.prototype.mainTasks = function() {
return "Driving to work, school, and the grocery store";
}
/* Constructor function for the SportsCar object */
function SportsCar() { }
/* SportsCar extends Vehicle */
SportsCar.prototype = new Vehicle();
/* SportsCar is lighter than Vehicle */
SportsCar.prototype.curbWeightInPounds = 3000;
/* SportsCar requires premium fuel */
SportsCar.prototype.refuel = function() {
return "Refueling SportsCar with premium 94 octane gasoline";
}
/* Function for performing the main tasks of a SportsCar */
SportsCar.prototype.mainTasks = function() {
return "Spirited driving, looking good, driving to the beach";
}
/* Constructor function for the CementTruck object */
function CementTruck() { }
/* CementTruck extends Vehicle */
CementTruck.prototype = new Vehicle();
/* CementTruck has 10 wheels and weighs 12,000 pounds*/
CementTruck.prototype.wheelCount = 10;
CementTruck.prototype.curbWeightInPounds = 12000;
/* CementTruck refuels with diesel fuel */
CementTruck.prototype.refuel = function() {
return "Refueling CementTruck with diesel fuel";
}
/* Function for performing the main tasks of a SportsCar */
CementTruck.prototype.mainTasks = function() {
return "Arrive at construction site, extend boom, deliver cement";
}