在面向对象语言中继承分成两种:接口继承和实现继承。解释一下,接口继承只继承方法的签名,而实现继承则是继承实际的方法。但是ECMAScript中的函数没有签名所以无法进行接口继承,只能是实现实现继承。而实现继承通常是依赖与原型链的。
原型链继承
原型的概念已经在上一篇中说过了。还是来简单的回顾一下构造函数,原型和实例的关系:每个构造函数都有一个圆形对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。但是如果让一个构造函数的原型指针指向另一个对象。那么这时的原型对象将包含一个指向另一个的原型的指针,如果层层递进的话就形成了原型链。
function SupType(){ this.property=true; } SupType.prototype.getSupValue=function(){ return this.property; } function SubType(){ this.subproperty=false; } SubType.prototype=new SupType(); SubType.prototype.getSubValue=function(){ return this.subproperty; } var instace=new SubType(); alert(instace.getSupValue());
这就是上面代码的原型图解。可以看到SubType的Prototype是指向一个SuperType的实例。SubTypePrototype并没有所谓的构造函数。这就是原型链的继承。当然不要忘记SuperType也是继承Object的。
可以用两个方式来进行判断原型和实例之间的关系。第一中是 instanceof操作符。
alert(instace instanceof Object); alert(instace instanceof SubType); alert(instace instanceof SupType);返回为true则为实例是原型链中出现过的构造函数。
还有一种就是isPrototypeof()方法。
alert(Object.prototype.isPrototypeOf(instace)); alert(SubType.prototype.isPrototypeOf(instace)); alert(SupType.prototype.isPrototypeOf(instace));
原型链和原型模式的构造函数有着一样的缺点,所有的引用值类型会被所有的实例所引用。而且不能给父类的构造函数传参数。所以就会借用构造函数的方法以组合的方法进行函数的继承。
借用构造函数的方式
其实这种原理十分简单就是在子类型的构造函数内部调超类型的构造函数。
function SupType(){ this.colors=["red","blue","green"]; } function SubType(){ SupType.call(this); } var instance1=new SubType(); instance1.colors.push("black"); alert(instance1.colors); var instance2=new SubType(); alert(instance2.colors);
在上面的代码中,在instance1中new的时候this就是intance1,然后调用了父类SupType的构造函数,在this的属性上添加了colors的属性。然后返回给instance1。这样每个实例都有独立的colors属性。
组合继承
顾名思义就是将使用原型链和构造函数两种进行组合继承。
function SupType(name){ this.name=name; this.colors=["red","blue","green"]; } SubType.prototype.sayName=function(){ alert(this.name); } function SubType(name,age) { SupType.call(this,name); this.age=age; } SubType.prototype=new SupType(); SubType.prototype.constructor=SubType; SubType.prototype.sayAge=function(){ alert(this.age); }在上面的代码中,需要共享的就添加到父类的原型中如方法SayName,不需要共享的就添加到构造函数中。在子类的构造函数中会有调用父类的构造函数,而且能传入参数。进行参数的初始化。这是JavaScript中最常用的继承模式。
原型式继承
原型式继承的原理就是一个对象可以作为另一个对象的基础。前者就是父类,后者是子类。看代码
//原型式继承 function object(o) { function F(){} F.prototype=o; return new F; } //定义一个实体 var person={ name:"shaoqi", friends:["ss","qq","rr"] } //调用方法 var anotherPerson=object(person); anotherPerson.name="G"; anotherPerson.friends.push("qq"); var yetanotherPerson=object(person); yetanotherPerson.name="G22"; yetanotherPerson.friends.push("qq22"); alert(person.friends)person就是anotherPerson和yetanotherPerson的父类,其实就是将子类的原型指针指向父类.那这个和原型链继承的差别是什么呢?在原型链中给子类的prototype是通过父类对象实例化的。而原型式中直接就是已有的对象。目的是什么呢?再基于已有对象创建新对象,同时还不必因此创建自定义的类型。
再ECMAScript5中通过Object.Create()的方法规范化了。不过这个原型链继承有着相同的缺点,就是所有的实例公用这同一个属性。
寄生式继承
寄生式继承和原型式继承关系紧密。其实就是在函数的内部对对象进行增强。
function createAnother(obj) { var clone=object(obj); clone.sayHi=function(){ alert("Hi!"); } return clone; }其实我也不知道这个寄生模式有什么用?有大神的话可以解释下 这个不就是原型式的继承然后再原型中添加自己定义的方法嘛。也有可能是为了下面这个模式。
寄生组合模式
其实在上面的组合模式也并不是完全没有确定。我们先回顾一下代码。
function SupType(name){ this.name=name; this.colors=["red","blue","green"]; } SubType.prototype.sayName=function(){ alert(this.name); } function SubType(name,age) { SupType.call(this,name); this.age=age; } SubType.prototype=new SupType(); SubType.prototype.constructor=SubType; SubType.prototype.sayAge=function(){ alert(this.age); }
在上面两个加粗的代码对SuperType进行了两次调用。简单来说就是再实例上name和colors被创建了一遍,然后再对象的实例上这两个属性又被创建了一遍。还是看图说话吧。这个是拍书的。将就的看下。
然后再对象上创建的实例属性屏蔽了原型上的实例。然后如何解决呢,那就是在组合模式中添加寄生模式。
function inheritPrototype(subType,SupType) { var prototype=Object(SupType.prototype);//创建对象 prototype.constructor=subType; subType.prototype=prototype; } function SupType(name){ this.name=name; this.colors=["red","blue","green"]; } SubType.prototype.sayName=function(){ alert(this.name); } function SubType(name,age) { SupType.call(this,name); this.age=age; } //SubType.prototype=new SupType(); inheritPrototype(SubType,SupType); SubType.prototype.constructor=SubType; SubType.prototype.sayAge=function(){ alert(this.age); }注意加粗的代码。调用了inheritPrototype这个方法。这个方法做了什么呢,它代替了new SuperType()这段代码。他没有去new 实例化创建属性,他复制了一份SuperType的prototype,然后将construction的指针指向subType。这样的话再SubType的prototype拥有了SuperType的原型方法,然后实例会调用SupType.call(this,name); 实例也有了在构造函数中定义的属性。
函数的构造就先写到这里。写的不好多多批评。