如上图:SuperType是一个函数,下面包括他的原型对象prototype,原型对象指向了构造函数的指针,而构造函数指回像了原型对象的内部指针,这样就形成了链式关系了。
就是说,当一个函数对象被创建时候,Function构造器产生的函数对象会运行类似这样的一行代码:
this.prototype = {constructor:this};
这个prototype对象是存放继承特征的地方。因为js没有提供一个方法去确定哪个函数是打算用来做构造器,所以每个函数都会得到一个prototype对象。constructor属性没有什么用,重要的是prototype对象。
实现原型链有一种基本模式,其代码大致如下:
function A(){
this.Aproperty = "111";
}
A.prototype.getA = function(){
return this.Aproperty;
};
function B(){
this.Bproperty = "222";
}
B.prototype = new A();//B继承A
B.prototype.getB = function(){
return this.Bproperty;
};
var C = new B();
console.log(C.getA());//111
以上定义了两个类型A和B。每个类型分别有一个属性和一个方法。它们的主要区别是B继承了A,而继承是通过创建A的实例,并将实例赋给B.prototype实现的。实现的本质是重写原型的对象,代之以一个新的类型的实例。换句话说,原来存在于A的实例中的所有属性和方法,现在也存在于B.prototype中了。在确立了继承关系之后,我们给B.prototype添加了一个方法,这样就继承A的属性和方法的基础上又添加了一个新方法。
如图:
另外一个很重要的链式继承模式:
function A(x){
this.x = x;
}
A.prototype.a = "a";
function B(x,y){
this.y = y;
A.call(this,x);
}
B.prototype.b1 = function(){
alert("b1");
}
B.prototype = new A();
B.prototype.b2 = function(){
alert("b2");
}
B.prototype.constructor = B;
var obj = new B(1,3);
就是说把B的原型指向了A的1个实例对象,这个实例对象具有x属性,为undefined,还具有a属性,值为"a"。所以B原型也具有了这2个属性(或者说,B和A建立了原型链,B是A的下级)。而因为方才的类继承,B的实例对象也具有了x属性,也就是说obj对象有2个同名的x属性,此时原型属性x要让位于实例对象属性x,所以obj.x是1,而非undefined。第13行又定义了原型方法b2,所以B原型也具有了b2。虽然第9~11行设置了原型方法b1,但是你会发现第12行执行后,B原型不再具有b1方法,也就是obj.b1是undefined。因为第12行使得B原型指向改变,原来具有b1的原型对象被抛弃,自然就没有b1了。
第12行执行完后,B原型(B.prototype)指向了A的实例对象,而A的实例对象的构造器是构造函数A,所以B.prototype.constructor就是构造对象A了(换句话说,A构造了B的原型)。
alert(B.prototype.constructor)出来后就是"function A(x){...}" 。同样地,obj.constructor也是A构造对象,alert(obj.constructor)出来后就是"function A(x){...}" ,也就是说B.prototype.constructor===obj.constructor(true),但是B.prototype===obj.constructor.prototype(false),因为前者是B的原型,具有成员:x,a,b2,后者是A的原型,具有成员:a。如何修正这个问题呢,就在第16行,将B原型的构造器重新指向了B构造函数,那么B.prototype===obj.constructor.prototype(true),都具有成员:x,a,b2。
如果没有第16行,那是不是obj = new B(1,3)会去调用A构造函数实例化呢?答案是否定的,你会发现obj.y=3,所以仍然是调用的B构造函数实例化的。虽然obj.constructor===A(true),但是对于new B()的行为来说,执行了上面所说的通过构造函数创建实例对象的3个步骤,第一步,创建空对象;第二步,obj.__proto__ === B.prototype,B.prototype是具有x,a,b2成员的,obj.constructor指向了B.prototype.constructor,即构造函数A;第三步,调用的构造函数B去设置和初始化成员,具有了属性x,y。虽然不加16行不影响obj的属性,但如上一段说,却影响obj.constructor和obj.constructor.prototype。所以在使用了原型继承后,要进行修正的操作。
关于第12、16行,总言之,第12行使得B原型继承了A的原型对象的所有成员,但是也使得B的实例对象的构造器的原型指向了A原型,所以要通过第16行修正这个缺陷。
文章说明:个人查看各种资料,原创所得,观点不一定准确,欢迎各路大牛勘误,小女子感激不尽。