许多OO语言支持两种继承方式:接口继承和实现继承。
接口继承只继承函数签名,实现继承则继承实际的方法。
由于函数无签名,在JavaScript中无法实现接口继承。所以只能实现方法继承。
实现继承主要依赖原型链。
什么是原型对象。我们知道每个构造函数一旦创建都有prototype指针指向它的原型对象(构造函数.prototype)。而原型对象(构造函数.prototype)会默认生成一个constructor指针又指向构造函数。在创建实例时,实例有一个内部属性[[prototype]]指向该原型对象。原型对象内创建的所有方法会被所有实例共享。
来看段代码:
function Person{
};
Person.prototype.name = "Nichloas";
Person.prototype.age = 29;
Person.prototype.job = "Software Enginner";
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
person1.sayName(); //Nichloas
var person2 = new Person();
person2.sayName(); //Nichloas
这样看着图,再结合上面的原型对象的解释就明白了。
还要说一点就是原型对象中的方法属性是被所有实例共享的。如果含有引用类型的属性,如数组,修改person1中的数组属性,也会导致person2中的该属性发生变化。
看一下什么是原型链。
原型链就是创建一个构造函数,它会默认生成一个prototype属性并指向原型对象。使用下一个构造函数的原型对象作为这个构造函数的实例。即 nextFuction.prototype = new thisFuction();
在下下一个构造函数的原型对象 = new nextFuction。这样下去就会构成一条实例与原型之间的链条,这就是原型链。
function SuperType(){
this.property = true;
}
//在SuperType函数的原型链上创建公共方法
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subProperty = false;
}
//继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
return this.subProperty;
}
var instance = new SubType();
alert(instance.getSuperValue()); //true;
函数之间的关系会是什么样子?
首先SuperType构造函数创建后会是这样子的:
SuperType函数本身内部会有一个prototype指针,指向SuperType Prototype(图中还没有画出来)而在构造函数创建之后,会按照某种规则生成一个原型对象即SuperType.prototype,该原型对象中默认也会有一个指针constructor再指向构造函数。由于在原型链上我们添加了一个getSuperValue函数,所以会存在原型对象中。
SuperType构造函数中还有一个属性property属性,后面解释。
然后创建了构造函数,再为其创建了属性subProperty。又使其 原型对象成为SuperType构造函数的实例。我们知道实例中会自动生成一个[[prototype]]的内部属性。所以就相当于该实例的默认方法重写了SubType.prototype(原型对象)。
然后再定义添加属性和方法到SubType.prototype中,最后SubType构造函数生成实例instance。
看最后的图:
现在来解释一下为什么property属性没有出现在SuperType的原型链中,而出现在了SubType的原型链中。subProperty没有出现在SubType的原型链中,而出现在了instance实例中。
因为我们知道在构造函数中定义的属性和方法实际上是实例的属性和方法。即只能出现在实例中。而SubType.prototype是SuperType的实例,所以property属性在其中。同理因为在构造函数SubType中定义的subProperty属性是实例属性,所以存在于instance中。实例中会有一个[[prototype]]内部属性指向构造函数的原型对象。这样就形成了一条链。
在通过原型链实现继承的情况下,当读取模式访问实例中的属性时,会先搜索实例,然后再搜索实例的原型,在一层一层知道找到或者到达原型链的末端停止。
其实我们上面的是少一环的,即Object。因为所有引用类型都是从object继承来的。
SubType继承了SuperType,SuperType继承了Object,当调用instance.toString方法,实际是调用了保存在Object中的方法。