?
JavaScript是一种功能强大的语言,起初它只是用于在浏览器中完成一定的Dom操作和特殊效果。随着AJAX和RIA技术的广泛普及,JavaScript发挥了越来越重要的作用,JavaScript的代码量越来越大,对可维护性的要求也越来越高。JavaScript提供了特有的类机制,但是在语法习惯上与传统面向对象的语言有很大的不同,这使得不少的JavaScript开发人员感到困惑,本文将会对JavaScript常见的类功能进行介绍。
1.JavaScript定义类及实例化
在JavaScript的语法中,并没有类似于Class的类定义关键字,而是使用与普通方法一样的function来定义类,所以定义普通的方法与定义类有着类似的语法(这使得刚接触JavaScript的开发人员会感到很难理解,关于如何判断function是要作为类的定义,可以查看这篇文章。)
?
//定义一个普通方法 function testSimpleFunction(){ alert("This is a simple function!"); } testSimpleFunction();//调用方法 //定义一个类 function Person(name,age){ this.name = name; this.age = age; this.aboutMe = function(){ alert("I’m a person!My name is "+this.name+",My age is "+this.age+"!") } } var personObject = new Person("levin",30);//实例化该类,得到一个对象 personObject. aboutMe();//调用对象的方法
?
?JavaScript是基于原型的语言,每个类(即构造函数)都有一个prototype属性指向该类对应的原型对象,而实例化得到的对象会拥有该类原型中的属性和方法。常用的定义类的方式如下:
?
function Person(name,age){ this.name = name; this.age = age; } Person.prototype.sayHello = function(){ alert(this.name); } var person = new Person("levin",30); person.sayHello();//调用原型中的方法
?
?对象在进行属性和方法访问时,首先会在对象内部查找,如果找不到,会在其构造函数原型中进行进一步的查找。基于这样的机制,对象从原型得到的属性和方法可以进行重写覆盖,对象就拥有与原型同名的属性和方法,而对于通过此类实例化的其它对象,则不会受其影响:
?
person.sayHello = function(){ alert("My own name is "+this.name); } person.sayHello(); var anotherPerson = new Person("zhangwb",30); anotherPerson.sayHello();
?在本例中,person对象和anotherPerson对象尽管都是通过Person类实例化而来,但是他们在调用sayHello方法时会有不同的结果。这就是因为person对象重写了从构造函数原型中继承而来的sayHello方法。
?
2.继承
在JavaScript中没有直接实现继承的关键字,因此关于继承有多种的实现方式,代表性的是类式继承和原型式继承,对于这两种继承方式,我们都会进行简单的介绍。
首先看一下类式继承。如果我们有一个名为Employee类想要继承Person类,一般的写法如下:
?
function Employee(name,age,workExperience){ Person.call(this,name,age) this.workExperience = workExperience; } Employee.prototype = new Person(); Employee.prototype.constructor=Employee Employee.prototype.getInput = function(){ return 5000; } var employee =new Employee("employee levin",30,4); employee.sayHello(); alert(employee.getInput());
?在这个例子中,我们定义名为Employee的类,要求继承Person类,该类有三个初始化参数,其中前两个与Person类一致,而第三个参数workExperience是该类特有的,Employee除了要继承Person类的属性和方法,还要求定义名为getInput的方法。
?
在本例中,我们在Employee的构造函数中,调用了Person的构造函数,并传递了适当的参数,从而实现了属性的继承。对于Person方法的继承,我们是通过调整Employee原型链的方式来达到的,首先将Employee类的prototype所指向的对象设置为一个Person实例,这样就使得Employee能够使用Person原型中定义的方法,但是这也把Employee原型中的构造函数指向了Person,因此我们需要将其进行重新置为Employee。通过以上的操作,Employee实现了对Person的继承,而Employee要定义新的方法,只需要在其prototype属性中继续追加即可。
这样的继承方式缺乏通用性,对于每个实现的继承的地方都写很多的类似代码,另外在调整原型链的时候,需要实例化一个父类的实例,而这是没有必要的,因为我们想要的仅仅是父类的原型链。因此我们可以写一个通用的方法:
?
function extend(subClass,superClass){ var F = function(){}; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; subClass.superClass = superClass.prototype; if(superClass.prototype.constructor == Object.prototype.constructor){ superClass.prototype.constructor = superClass; } }
?
?在这个方法中,传入两个参数,分别为子类和父类,我们首先定义了一个新的构造函数,让该函数的prototype设置为父类的prototype,然后子类的prototype属性指向了一个F的实例,这样就能够在不实例化父类的情况下完成原型链的调整,另外我们在子类中定义了一个名为superClass的属性,指向了父类的原型,从而可以通过子类调用到父类的方法。此时要实现Employee和Person的继承关系就容易许多了:
?
function Employee(name,age,workExperience){ Employee.superClass.constructor.call(this,name,age) this.workExperience = workExperience; } extend(Employee,Person); Employee.prototype.getInput = function(){ return 5000; }
?
?其次,我们来看一下原型式继承。原型式继承与类式继承以及其它语言的继承方式都有着很大的差异。在原型式继承中,不会再使用类来定义对象结构,而是直接使用字面量,以后创建的对象都以此为原型。如有以下的Person原型对象:
?
var Person={ name:"levin", age:30, getName:function(){ alert(this.name); } }
?
?要创建新对象时,需要调用以下的方法:
?
function object(o) { function F() {} F.prototype = o; return new F(); }
?
?在这个方法中,我们定义了一个空的构造函数,将传入对象作为构造函数的prototype属性,从而生成的新对象能够访问到原型提供的属性和方法。如我们要创建一个Person的实例:
?
var person = object(Person); //得到Person的实例 person.getName();
?
?而继承的实现也有赖于object方法,如要实现和前面介绍相同功能的Employee类,只需执行以下的代码即可:
?
var Employee = object(Person); Employee.workExperience = 4; Employee.getInput = function(){ alert(5000) } var employee = object(Employee);//得到Employee的实例 employee.getInput()
?以上简要介绍了JavaScript两种常见的继承方式,无论哪种方式与传统的面向对象语言都有很大的差异,这需要开发人员对JavaScript有较为深入的了解,而一些常用的框架都对JavaScript的类机制进行了封装,这样我们就能够更方便的定义和使用JavaScript类。
?
?
在其它面向对象语言中,发挥重要功能的接口功能,在JavaScript中没有对应的关键字实现,但是有很多的替代方案进行了模拟,《JavaScript设计模式》一书对此有所介绍,感兴趣的读者可以参考。
?
根据以上的介绍,我们了解到在JavaScript中实现类机制需要对该语言有更为深入的掌握,而JavaScript作为一种动态语言,也提供了其它静态面向对象语言所难以实现的功能,这也许正是其有趣和吸引人的地方吧。
?
参考资料:
《JavaScript权威指南》 David Flanagan著 张铭泽译
《JavaScript设计模式》 Ross Harmes、Dustin Diaz著 谢廷晟译
http://javascript.crockford.com/prototypal.html
http://javascript.crockford.com/inheritance.html
http://docs.dojocampus.org/
http://blog.csdn.net/dojotoolkit/
http://dojotoolkit.org/
?
?