今天在图书馆看《Ajax企业级开发》这本书时,看到它关于javascript中继承的一些见解,觉得非常不错。并且提出了几点关于在编写javascript继承封装函数的方式时候应该达到的几个目标:
- 避免在原型阶段调用基类的构造函数。就是在子类实现继承的时候,避免在子类中调用了基类的构造函数来实现继承基类的属性。这点考虑是并无道理的,如果基类的构造函数中有关于DOM操作的执行代码,并且修改了页面的DOM结构,这就会失去了继承的意义了。
- 避免子类对父类方法的全局调用。就是避免在子类中直接使用基类的名称去调用基类的方法,这样造成对基类名称的依赖性过大,扩展性和维护性不佳。
- 允许调用基类方法和构造函数。
- 不要扩展Object.prototype属性。这个主要是避免增加Object类的方法,使得Object类越来越难操纵,尽量保持它的“纯净”,把对性能的影响效果降到最低。
- 确保不会严重影响性能。
上面罗列的五点,非常具有参考意义。虽然是一些细节问题,可是对于企业级的应用来说,有时候可能就是问题所在。在继承方面,Dean Edwards和Douglas Crockford也做了比较详细的叙述:《http://dean.edwards.name/weblog/2006/03/base/》,《Classical Inheritance in JavaScript》,同时,John Resig也做了相关的叙述:《Simple JavaScript Inheritance》,都非常经典而且具有代表意义。
书中也根据上面的五点,给出了一个extend函数示例:
[javascript]
var extend=function(subClass,baseClass){
var F=function(){};
F.prototype=baseClass.prototype;
subClass.prototype=new F();
subClass.prototype.constructor=subClass;
subClass.base=baseClass; //本人修改,书中是baseClass.prototype
return subClass;
}
[/javascript]
上面使用了一个临时类F,通过这个临时类,避免了在extend函数内部调用基类的构造函数。通过设置subClass的一个静态方法base来储存基类的引用,这样就可以在子类中调用基类的方法和构造函数,也避免了子类对基类方法的全局调用。extend内部也没有扩展Object.prototype属性。在性能方面,也没有造成什么过大的影响。看起来是比较良好的了。
但是我的问题来了,如果要实现“避免在原型阶段调用基类的构造函数”这个目标,对于在继承基类中通过this声明的属性是行不通的,比如:
[javascript]
var baseClass=function(name,age){
this.name=name;
this.age=age;
}
[/javascript]
就像上面代码所示,如果要避免在子类中调用基类的构造函数的话,上面的name和age属性是不可能被子类继承的。
[javascript]
extend(subClass,baseClass);
function subClass(){};
var sub=new subClass();
alert(sub.name); // return "undefined"
[/javascript]
要继承基类中通过this声明的属性,就必须要在子类中调用基类的构造函数:
[javascript]
extend(subClass,baseClass);
function subClass(name,age,grade){
subClass.base.call(this,name,age);//调用基类的构造函数来继承属性
this.grade=grade;
};
[/javascript]
如果没必要通过在基类里通过this来声明属性的话,可以通过prototype在构造函数外部来实现定义属性和方法,这也是从一个原因出发的:尽量保持构造函数的简单,使得类的实例化更加快。这样也可以避免在子类中调用基类的构造函数
[javascript]
var baseClass=function(){};
baseClass.prototype={
name:"supesha",
age:25,
getName:function(){
return this.name;
}
}
extend(subClass,baseClass);
function subClass(grade){
this.grade=grade;
};
alert((new subClass("two")).name);
[/javascript]
如果是如上所述的情况的话,不需要在基类中通过this来声明属性,那么使用foreach循环也是可以实现继承方法的,这样就可以避免使用一个临时类来做“中间人”啦,缺点就是兼容性不好,不能同时兼容使用this声明属性的情况:
[javascript]
var extend=function(subClass,baseClass){
for(var key in baseClass.prototype){
subClass.prototype[key]=baseClass.prototype[key];
}
subClass.base=baseClass;
return subClass;
}
var subClass=extend(function(){},baseClass);
[/javascript]
使用哪种方式,这个可能需要在应用的整体中去考究了。
因此,选择实现继承的方式应该遵循上面罗列的五点目标是十分必要的。实现方式多种多样,但是每一种都是在改善原来的继承方式,使得javascript继承更加的强大。
Just want to say what a great blog you got here!
I’ve been around for quite a lot of time, but finally decided to show my appreciation of your work!
Thumbs up, and keep it going!
Cheers
Christian, iwspo.net
写得好,但本人领悟有限。