在面向对象编程中,语言自身都有一套严格的封装机制,开发人员只是按惯性思维去开发具体的项目,很少关心封装问题,因为语言会自动完成基本的功能封装,当然具体应用的功能还需要程序人员自己去封装。但是,JavaScript语言没有提供良好的封装机制,只能够依靠开发人员的方法来实现部分功能封装。类继承是在JavaScript程序开发中应用得比较广泛的继承模式,为了更方便地使用,建议读者对这种模式进行规范和封装,以便提高代码利用率。
首先,定义一个封装函数。设计入口为子类和超类对象,函数功能是子类能够继承超类的所有原型成员,不设计出口:
function extend(Sub,Sup){//类继承封装函数
//参数Sub表示子类,Sup表示超类
}
在函数体内,首先定义一个空函数F,用来实现功能中转。设计F的原型为超类的原型,然后把空函数的实例传递给子类的原型,这样就避免了直接实例化超类可能带来的系统负荷。在实际开发中,超类的规模可能会很大,进行实例化会占用大量内存。
恢复子类原型的构造器子类,同时,检测超类的原型构造器是否与Object的原型构造器发生耦合,如果是,则恢复它的构造器为超类自身。
function extend(Sub,Sup){//类继承封装函数
var F=function{};//定义一个空函数
F.prototype=Sup.prototype;//设置空函数的原型为超类的原型
Sub.prototype=new F;//实例化空函数,并把超类原型引用传递给子类
Sub.prototype.constructor=Sub;//恢复子类原型的构造器为子类自身
Sub.sup=Sup.prototype;//在子类中存储超类原型,避免子类和超类耦合
if(Sup.prototype.constructor==Object.prototype.constructor){//检测超类原型构造器是否为自身
Sup.prototype.constructor=Sup//类继承封装函数
}
}
一个简单的功能封装函数就这样实现了。下面定义两个类,尝试把它们绑定为继承关系。
function A(x){//构造函数A
this.x=x;
this.get=function{
return this.x;
}
}
A.prototype.add=function{
return this.x+this.x;
}
A.prototype.mul=function{
return this.x*this.x;
}
function B(x){//构造函数B
A.call(this,x);//在函数体内调用构造函数A,实现内部数据绑定
}
extend(B,A);//调用类继承封装函数,把A和B的原型捆绑在一起
var f=new B(5);
alert(f.get)//5
alert(f.add)//10
alert(f.mul)//25
在类继承封装函数中,有这样的语句“Sub.sup=Sup.prototype;”,在上面的代码中没有体现,为了理解它的价值,先看下面的代码:
extend(B,A);
B.prototype.add=function{//为B类定义一个原型方法
return this.x+""+this.x
}
上面的代码是在调用封装函数之后再为B类定义了一个原型方法,该方法名与A类的原型方法add同名,但功能不同。如果此时测试程序,那么会发现子类B定义的原型方法add将会覆盖超类A的原型方法add,如下:
alert(f.add)//字符串55,而不是数值10
在B类的原型方法add中调用超类的原型方法add,从而避免代码耦合的现象发生:
B.prototype.add=function{
return B.sup.add.call(this);//在函数内部调用超类的方法add
}