背景知识
继承是OOP中非常有名的概念(但不是必要的条件)。基本上目前有两种继承方式,接口继承(Java的Interface)和实现继承。接口继承只继承函数签名,实现继承继承实际的方法。而JavaScript没有函数签名,所以只支持实现继承。而JavaScript的实现继承主要是通过原型链来实现的。
原型链
JavaScript是利用原型来让一个引用类型继承另一个引用类型的属性和方法。
我们简单回顾一下构造函数、原型和实例的关系:
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
而原型链的思想就是让原型对象等于另一个类型的实例。层层递进。
function SuperType(){
this.property = true;
}
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
上图是各个对象之间的关系。另外,我们知道每个对象都有constructor属性,因为是继承下来的。而现在instance.constructor指向SuperType。原型链的搜索模式和之前的原型搜索机制一样。
默认的原型
实际上,我们上图的原型链缺少一环。也就是所有引用类型默认继承的Object,这个继承也是通过原型链来实现的。比如,所有函数的默认原型都是Object的实例。这也是所有自定义类型都会继承toString()和valueOf()等默认方法的根本原因。下面是完整的原型链。
确定原型和实例的关系
通常有两种方式来确定原型和实例的关系。第一个使用instance操作符,第二个使用isPrototypeOf()方法。
使用instance操作符:
alert(instance instanceof Object); //true
alert(instance instanceof SubType); // true
使用isPrototypeOf()方法(只要是原型链中出现过的原型都可以说是该原型链所派生的实例的原型):
alert(Object.prototype.isPrototypeOf(instance));//true
alert(SubType.prototype.isPrototypeOf(instance));//true
谨慎地定义方法
通常来说,子类型有时候需要重写父类型的某个方法或者定义自己的方法(父类型中不存在)。需要注意的是给原型添加方法的代码一定要在替换原型的语句之后(因为原型链实现的原理):
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//继承了 SuperType
SubType.prototype = new SuperType();
//添加新方法
Sub