本文深入解析JavaScript中的类式继承机制。通过对比仅使用构造函数调用与结合原型链赋值的两种方式,详细说明了如何同时继承实例属性和原型方法。文章提供了完整的代码示例,解释了修正constructor指向的必要性,并封装了通用的extend函数以简化子类扩展过程,帮助开发者掌握经典的JS面向对象编程技巧。
类式继承的目标与基础概念
在开始编写代码前,明确设计目标至关重要。通常,设计类的核心目的是消除冗余代码。利用继承机制,开发者可以在现有类的基础上进行扩展,复用已实现的方法,从而降低修改设计的复杂度。以下将通过具体示例来阐述这一概念。
代码实现如下:
function Person(name){
this.name = name;
}
Person.prototype.getname = function(){
return this.name;
}
function Bloger(name,blog){
Person.call(this,name);
this.blog = blog;
}
var bloger = new Bloger("zhenn","https://www.jb51.net");
alert(bloger.name=="zhenn"); /*返回ture*/
alert(bloger.blog) /*提示https://www.jb51.net*/
alert(bloger.getname()=="zhenn"); /*提示"bloger.getname is not a function"*/
仅使用构造函数调用的局限性
通过上例可以看到,Bloger在其内部通过call动态调用了其父类Person的原生属性和方法(关于call的讲解请参考https://www.jb51.net/article/62086.htm),即可以理解为Bloger继承了Person,成为它的一个子类,但是细心的同学会发现,Person原型对象中的方法,仅仅依靠call,是不能继承过来的,这也就是会提示"bloger.getname is not a function"的原因所在了。不过不用担心,对上述代码稍作处理,即可解决这个问题!
结合原型链与构造函数的完整继承方案
修正后的代码如下:
function Person(name){
this.name = name;
}
Person.prototype.getname = function(){
return this.name;
}
function Bloger(name,blog){
Person.call(this,name);
this.blog = blog;
}
/*请注意以下两行代码*/
Bloger.prototype = new Person();
Bloger.prototype.constructor = Bloger;
var bloger = new Bloger("zhenn","https://www.jb51.net");
alert(bloger.name=="zhenn"); /*返回ture*/
alert(bloger.blog) /*提示https://www.jb51.net*/
alert(bloger.getname()=="zhenn"); /*提示true*/
这里重点解释新增的两行代码。每个构造函数都拥有prototype属性,该属性指向原型对象。原型对象本质上也是一个实例,其定义的属性和方法可供所有实例共享。新添加的代码意图是将子类的prototype指向父类的一个实例。由于父类实例已包含父类原型上的所有属性和方法,此举使子类原型继承了这些内容,从而实现了完整继承。
此外需注意Bloger.prototype.constructor = Bloger;这一行。当子类的prototype被设置为父类实例时,其constructor属性会意外指向父类。因此,必须手动将子类原型的constructor重新指向子类本身。至此,JavaScript的类式继承机制已完整实现。
封装通用的extend继承函数
为了简化子类的定义,可以将扩展过程封装为一个名为extend的函数。该函数的作用是基于给定的父类结构创建新的子类:
代码实现如下:
function extend(childClass,parentClass){
var F = new Function();
F.prototype = parentClass.prototype;
childClass.prototype = new F();
childClass.prototype.constructor = childClass;
}
利用这个extend函数,可以便捷地扩展子类。只需调用extend(Bloger,Person),即可实现与前文相同的完全继承效果,无需手动编写那两行关键代码。