原型链做为实现继承的主要方法:其基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。javascript
首先咱们得清楚构造函数(constructor),原型对象(prototype)和实例的三者关系。html
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。因此当咱们让一个构造函数的原型对象等于另外一个类型的实例java
function A(){}; function B(){}; B.prototype=new A(); var instance=new B();
instance的原型对象是B.prototype,而B.prototype又等于构造函数A的一个实例对象,A的实例对象的原型对象是A.prototype,这样就造成了一条原型链了。编程
实例:数组
function SuperType(){//定义一个SuperType类型 this.property = true;//给实例对象添加一个属性 } SuperType.prototype.getSuperValue = function(){//给原型对象添加一个方法 return this.property; }; function SubType(){//定义一个SubType类型 this.subproperty = false; } //inherit from SuperType SubType.prototype = new SuperType();//建立一个SuperType的实例对象,并把它赋值给SubType.prototype SubType.prototype.getSubValue = function (){//给原型对象添加一个方法 return this.subproperty;//返回实例属性 }; var instance = new SubType();//建立一个对象实例 alert(instance.getSuperValue()); //true
建立一个SuperType的实例对象,并把它赋值给SubType.prototype,也就是说,原来存在于SuperType的实例对象中的全部属性和方法,如今也存在于SubType.prototype中了,咱们在给SubType.prototype添加了一个方法,这样就在继承SuperType的属性和方法的基础上又添加了一个新方法。函数
咱们没有使用SubType默认提供的原型,而是给它换了一个新原型;这个新原型就是SuperType的实例。因而,新原型不只具备做为一个SuperType的实例所拥有的所有属性和方法,并且其内部还有一个指针,指向了SuperType的原型。this
如今instance指向SubType的原型,Subtype的原型指向SuperType的原型。getSuperValue()方法仍然在SuperType.prototype中,但property则位于SubType.prototype中了。由于property是一个实例属性,而getSuperValue()方法则是一个原型方法。既然SubType.prototype如今是SuperType的实例,那么property固然就位于该实例中了。prototype
instance.constructor此时指向的是SuperType,这是由于原来的SubType.prototype被重写的缘故。任何一个prototype对象都有一个constructor属性,指向它的构造函数。若是没有"SubType.prototype = new SuperType();"这一行,SubType.prototype.constructor是指向SubType的;加了这一行之后,SubType.prototype.constructor指向SuperType。更重要的是,每个实例也有一个constructor属性,默认调用prototype对象的constructor属性。所以,在运行"SubType.prototype = new SuperType();"这一行以后,instance.constructor也指向SuperType!这显然会致使继承链的紊乱(instance明明是用构造函数SubType生成的),若是咱们想要彻底符合继承,能够将将SuperType.prototype对象的constructor值改成SuperType。设计
经过原型实现继承时,不能使用对象字面量建立原型方法,由于这样会重写原型链指针
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; }, someOtherMethod : function (){ return false; } }; var instance = new SubType(); alert(instance.getSuperValue()); //error!
原型链虽然很是强大,可是它也存在一些问题,其中最大的问题就是:包含引用类型的原型属性会被全部实例共享。
而这也正是为何要在构造函数中,而不是在原型对象定义属性的缘由。在经过原型来实现继承时,原型实际上会变成另外一个类型的实例。因而,原先的实例属性也就变成了如今的原型属性了。
function SuperType(){ this.colors = ["red", "blue", "green"]; } function SubType(){ } //inherit from SuperType SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green,black"
SuperType定义了一个colors的实例对象属性,该属性包含一个数组(引用类型值),SuperType的每一个对实例都会有本身的colors属性。当SubType经过原型链继承了SuperType以后,SubType.prototype就成了SuperType的一个实例,所以它也拥有了一个它本身的colors属性,就更专门建立了SubType.prototype.colors属性同样。也正由于如此,因此SubType构造函数的实例对象都会经过原型链继承这个属性,当其中某个实例对象改变了这个属性,在其余实例中也会体现出来。
在看看下面这种状况
function SuperType(){ this.colors = ["red", "blue", "green"]; } function SubType(){ } //inherit from SuperType SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors= ["blue", "green"];//重写了colors属性,如今这个属性是属于instance1本身的,也就是再也不是对原型对象上的属性引用了 alert(instance1.colors); //"blue,green" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green"//引用(继承)原型对象上的属性,实际属性仍是部署在原型对象上的。
这个实例与前面的实例有什么区别:colors都是原型属性,因此每一个实例对象都会继承这个属性,若是没有重写这个属性,那么实例对象的这个属性都是对原型对象的该属性的引用,改变它会反映到其余实例对象中,若是重写了,那么这个属性就会覆盖原来的属性,从而变成自身的属性。
(1)经过instanceof
alert(instance instanceof Object) //true
(2)经过isPrototypeof
alert(Object.prototype.isPrototypeof(instance)); //true
参考:
(1)JavaScript高级程序设计第六章