先说说组合继承。最常用的继承方式组合继承,其最大的问题是无论在什么情况下,都会调用两次超类型的构造函数:一次是在创建子类原型的时候,另一次是在子类型构造函数内部。
组合继承是通过原型继承方法和原型属性,构造函数继承实例属性。但子类通过原型也继承了超类型的全部实例属性(方法暂且不说),即超类的实例属性成为子类的原型属性,所以不得不在调用子类构造函数时重写这些属性。也就是说在子类的原型对象上继承来自超类的实例属性完全是多余的。
看一个组合继承的例子。
function SuperType(name){ this.name = name; this.colors = [‘red‘,‘blue‘,‘green‘]; } SuperType.prototype.sayName = function(){ alert(this.name); } function SubType(name,age){ SuperType.call(this,name); //第二次调用SuperType() this.age = age; } SubType.prototype = new SuperType(); //第一次调用SuperType() SubType.prototype.constructor = SubType; SubType.prototype.sayAge=function(){ alert(this.age); }; var instance = new SubType(‘Greg‘,39); //调用SubType构造函数,重写原型属性instance.colors.push(‘black‘); //重写原型属性
在第一次调用SuperType构造函数时,SubType.prototype得到两个属性,name和colors(这两个通过原型继承来的属性是多余的)。当创建instance实例调用SubType的构造函数时会再一次调用SuperType的构造函数,这一次又在新对象上创建了实例属性name和colors,也就是重写了原型对象的属性,屏蔽了原型中两个同名属性。
为了避免这种两次调用超类构造函数导致子类原型对象创建了多余属性的缺陷,可以使用寄生组合式继承。
什么是寄生组合式继承?即通过借用构造函数来继承属性,通过原型链的混成方式来继承方法。
基本思路是:不必为了指定子类型的原型而调用构造函数,我们所需要的无非是超类型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后将结果指定给子类型的原型。
基本模式如下:
还是要用之前的 object函数。
function object(o){ function F(){}; F.prototype = o; return new F(); }
接着是寄生组合式继承的基本模式。
function inheritPrototype(subType,superType){ var prototype = object(superType.prototype); //创建对象 prototype.constructor = subType; //增强对象 subType.prototype = prototype; //指定对象 }
此函数接收两个参数,子类构造函数和超类构造函数。函数内部,第一步创建超类原型对象的一个副本,第二步为副本添加constructor属性,使其指向subType,弥补因为重写原型而失去默认的constructor属性。最后一步,将副本赋值给子类型的原型。整个过程说的简单点,就是将超类原型对象的一个副本复制给子类的原型对象。这样一来就可以避免继承超类的实例属性,也就是避免了在子类原型对象上创建多余的属性了。再举一个例子。
function superType(name){ this.name = namel this.colors = [‘red‘,‘blue‘,‘yellow‘]; } superType.prototype.sayName = function(){ alert(this.name); } function subType(name, age){ superType.call(this,name); //通过构造函数继承实例属性 this.age = age; } inheritPrototype(subType,superType); //仅调用一次超类构造函数 subtype.prototype.sayAge = function(){ alert(this.age); }
这个例子的高效率体现在它只调用了一次superType构造函数,并且避免了在subType.prototype上创建不必要的属性。与此同时,原型链还能保持不变,因此可以正常使用instanceof()和isPrototypeOf()。寄生式组合继承是引用类型最理想的继承范式。