JavaScript面向对象编程实现研究

来源:互联网 发布:淘宝店铺出售转让 编辑:程序博客网 时间:2024/05/09 04:12
 
JavaScript是一种解释性的脚本语言,所以运行前不需要进行任何的编译,可以使用各种文本编辑器直接进行编译,保存后就可以进行运行。可以说JavaScript是一种轻型的编程语言,所以对于一些高级语言如(Java、C#等)所提供的面向对象编程特性的支持就不是很直接、自然,需要使用一些特殊的机制来实现,本文介绍了在JavaScript中实现面向对象的几个要点:
1.      使用prototype来实现类的继承
JavaScript通过一种链接机制来支持继承,而不是通过完全面向对象语言(如C#
)所支持的基于类的继承模型。每个JavaScript对象都有一个内置的属性,名为prototype。prototype属性保存着对另一个JavaScript对象的引用,这个对象作为当前对象的父对象。例如:我们可以通过如下的方式来实现SportsCar类对Vehicle类的继承:
/* SportsCar extends Vehicle */
SportsCar.prototype = new Vehicle();
2.      使用prototype来实现对象函数或者属性的重写
当我们通过上面的方式实现了类的继承之后,如果对于某个函数或者属性子类需要提供与父类不一样的实现时,我们仍然可以采用prototype来提供新的实现,如下面的代码:
SportsCar.prototype.refuel = function() {
       return "Refueling SportsCar with premium 94 octane gasoline";
}
虽然在父类中提供了refuel()函数的实现,当创建子类对象时,调用refuel函数时,执行的是上面的实现。
       当通过点记法引用对象的一个函数或者属性时,倘若对象上没有这个函数或者属性,此时就会使用对象的prototype属性。当出现这种情况时,将检查对象prototype属性所引用的对象,查看是否有所请求的函数或者属性。如果prototype属性引用的对象也没有所需要的函数或者属性,则会进一步检查这个对象(prototype属性引用的对象)的prototype属性,依次沿着链向上查找,直到找到所请求的函数或者属性,或者到达链尾,如果到达链尾还没有找到,则返回undefined。从这个意义上讲,这种继承结构更应是一种“has a”关系,而不是“is a”关系。
       prototype机制是动态的,可以根据需要在运行时配置,而无须重新编译。你可以只在需要时才向对象增加属性和函数,而且能动态地把单独的函数合并在一起,来创建动态、全能的对象。对prototype机制的这种高度动态性可谓褒贬不一,因为这种机制学习和应用起来很不容易,但是一旦正确地加以应用,这种机制则相当强大而且非常健壮。
       这种动态性与基于类的继承机制中的多态概念异曲同工。两个对象可以有相同的属性和函数,但是函数方法(实现)可以完全不同,而且属性可以支持完全不同的数据类型。这种多态性使得JavaScript对象能够有其他脚本和函数以统一的方式处理。
3.      如何在JavaScript中实现私有属性和信息的隐藏
铁杆的面向对象设计的支持者会注意到,当使用prototype方法向JavaScript对象增加属性和函数时,所增加的函数和属性都是公用的,所有其他的对象都能访问。对于函数来说,这通常没有什么问题,因为大多数的函数都确实应该提供给外部的客户。但是对于属性,面向对象设计的支持者就会指出,共有属性违反了信息隐藏的概念,对象的属性应当是私有的,因此外部客户不能直接访问。外部客户只能通过共用可用的函数来访问对象的私有属性。对此Douglas Crockford提出了一种在JavaScript中创建私有属性的方法,如下:
l         私有属性可以在构造函数中使用var关键字定义;
l         私有属性只能由特权函数(privileded function公用访问;
特权函数就是在构造函数中使用this关键字定义的函数。外部客户可以访问特权函数,而特权函数可以访问对象的私有属性。
例如下面的代码提供了getXXX()和setXXX()函数来访问对象的私有属性:
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;
       }    
}
       注意wheelCount和curbWeightInPounds属性都在构造函数中使用了var关键字定义,这就使得这两个属性是私有属性。属性不再是公有的,如果还通过点记法访问wheelCount属性的值,如下:
       var numberOfWheels = vehicle.wheelCount;
就会返回undefined,而不是wheelCount的实际值。因为属性是私有的,所以要访问它们只能使用对应的公有函数。
4.      JavaScript中基于类的继承的实现
JavaScript中基于prototype的继承机制可以很好的工作,但是对于一些习惯于C++和Java等面向对象的高级语言中基于类的继承机制的人来说,JavaScript的prototype继承机制不是一种很自然的编程方法。对此NetScape的Bob Clary提出了一种方法,它可以使一个对象使用一个通用的脚本从另一个对象继承属性和函数。脚本如下:
function createInheritance(parent,child)
{
       var property;
       for(property in parent) {
              child[property] = parent[property];
       }
}
这个脚本只是将“父”对象的属性和函数简单的复制到“子”对象。这样我们就可以在子对象上调用“父”对象上定义的函数和属性了。但是有时候“子”类可能要提供同样方法的不同于“父”类的实现,上面的这种方式将会覆盖在“子”中提供的实现,所以我们应该将“子”对象中不存在的函数和属性复制到子对象,这样一来,子对象中的函数或者属性就能覆盖“父”对象的实现,从而实现了类的多态效应。
function createInheritance(parent,child)
{
       var property;
       for(property in parent) {
              if(!child[property]) child[property] = parent[property];
       }
}
使用这种方式要求我们在创建“子”类对象以后,要显式的调用上面的方法来实现类的继承。
       var sportsCar = new SportsCar();
       createInheritance(new Vehicle(),sportsCar);
这种使用对象的[‘…’]的方式只能对自定义的JavaScript对象的函数或者属性访问,对于内置的类型如:String、Date等不能够访问的到其包含的函数或者属性成员。
5.      prototype function 和 priveliged function
l         prototype是对已构建的类进行新的功能扩展
例如在String的对象中并没有提供首尾空字符串的剪切,这是我们就可以通过prototype方式来提供这些功能的扩展,如下:
String.prototype.lTrim = function(){return this.replace(/(^/s*)/g,'');}
String.prototype.rTrim = function(){return this.replace(/ (/s*$)/g,'');}
String.prototype.trim = function(){return this.replace(/(^/s*)|(/s*$)/g,'');}
所以如果“父”类没有提供我需要的函数,我可以通过prototype来提供这种函数的扩展。当然在类的原始定义中,同样可以使用prototype来定义函数。
l         priveliged function则是在类的构造函数中就要提供的,所以应该是类原生的函数
在类的定义中,我们在类的构造函数中提供这种特权函数的实现,所以它具有更高的优先访问权,也就是说,如果在类的定义中,我们同时提供了prototype function 和priveliged function,通过对象调用函数时,将会调用priveliged function,而会忽略prototype function。
l         两者都可以通过“对象[‘函数名称或者属性名称’]”访问得到
由于两者都是对象的成员,所以都可以通过这种方式访问。但如果同时提供了这两种形式的函数,运行时会忽略prototype function。
 
原创粉丝点击