我们知道继承是oo语言中不可缺少的一部分,对于JavaScript也是如此。一般的继承有两种方式:其一,接口继承,只继承方法的签名;其二,实现继承,继承实际的方法。JavaScript不支持签名,因此只有实现继承。其中实现继承主要是依赖于原型链的。下面我将以原型链为重点说说继承的几种主要的方式:
原型链继承
借用构造函数继承
组合继承(重点)
第一部分:原型链继承
A
要说原型链继承,不得不首先介绍一下原型链的概念。
想象一下,如果使原型对象等于另一个对象的实例,则此时原型对象将包含一个指向另一个原型的指针。相应地,另一个原型也将包含指向另一个构造函数的指针。假设另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条(注意:这里的实例和原型都是相对的),这便是原型链的基本概念。
function SuperType(){
this.property=true;
}
SuperType.prototype.getSuperValue=function(){
return this.property;
};
function SubType(){
this.subproperty=false;
}
SubType.prototype=new SuperType();
SubType.prototype.getSubvalue=function(){
return this.subproperty;
}
var instance=new SubType();
console.log(instance.getSuperValue());//true
在上述代码中,我们可以看出subType的原型是SuperType的实例,因此,原来存在于SuperType的实例中的所有属性和方法,现在也存在于SubType.prototype中了。且我们没有使用SubType默认提供的原型对象,而是给它换了一个新原型对象(即SuperType的实例)。因此,新原型对象不仅具有作为一个SuperType的实例所拥有的全部属性和方法,而且其内部还有一个指针,指向了SuperType的原型。即:instance指向SubType的原型,SubType的原型指向了SuperType的原型。值得注意的是:property现在位于SubType.protoType中(因为SuperType构造函数中的this指向的是创建的对象实例)。
当以读取模式访问一个实例属性时,搜索过程会沿着原型链向上进行搜索。比如,调用instance.getSuperValue()会经历三个搜索步骤:(1).搜索实例中是否存在该方法,结果:无。(2).沿着原型链向上,搜索SubType.prototype中是否存在该方法,结果:无。(3).继续沿着原型链,搜索SuperType.prototype中是否存在该方法,结果:存在。于是停止搜索。也就是说:在找不到属性或方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来。
注意:instance.constructor现在指向的是SuperType,这是因为SubType的原型指向了另一个对象--SuperType的原型,而这个原型对象的constructor属性指向的是SuperType。我们可以用以下代码做出验证:
console.log(instance.constructor);
最终返回的是SuperType这个构造函数。
重要:别忘记默认的原型。我们知道,所有的引用类型都继承了Object,而这个继承也是通过原型链实现的,即所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype。这也是所有引用类型都会继承toString()、valueOf()方法的根本原因。我们可以使用下面代码做出验证:
console.log(Object.prototype.isPrototypeOf(instance));//true
console.log(SuperType.prototype.isPrototypeOf(instance));//true
console.log(SubType.prototype.isPrototypeOf(instance));//true
也就是说instace实例对象的原型对象分别是Object.prototype、SuperType.prototype、SubType.prototype。另外我们还可以使用instanceof操作符判断,实例instance与构造函数之间的关系,如下所示:
console.log(instance instanceof Object);//true
console.log(instance instanceof SuperType);//true
console.log(instance instanceof SubType);//true
即instance是Object SuperType SubType的实例。下面我们使用一张图表表示他们之间的关系。
这里,我们可以认为加粗的线条就是原型链(实例与原型的链条)。
从这张图表中,我们可以看到SubType Prototype是没有constructer属性的,更没有指向SubType构造函数,这是因为创建SubType构造函数同时创建的原型对象和这个原型对象不是同一个,这个原型对象是SuperType的实例。注意到,后两个原型对象都有一个[[prototype]]属性,因为这时他们是被当作实例来处理的。
B
谨慎地定义方法