许多OO 语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。如前所述,由于函数没有签名,在ECMAScript 中无法实现接口继承。ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链来实现的。 1.原型链 SubType 继承了SuperType,继承是通过创建SuperType 的实例,并将该实例赋给SubType.prototype 实现的。实现的本质是重写原型对象,代之以一个新类型的实例。
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注意:所有引用类型默认都继承了Object(也是通过原型链实现的),SubType 继承了SuperType,而SuperType 继承了Object。当调用instance.toString()时,实际上调用的是保存在Object.prototype 中的那个方法。
alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true跟创建对象一样,SubType 的所有实例都会共享一个引用类型属性。为了解决这个问题很少单独使用原型链。
2.经典继承(借用构造函数)
function SuperType(){ this.colors = ["red", "blue", "green"]; } function SubType(){ //继承了SuperType SuperType.call(this); } 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"无法函数复用。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。
3.组合继承(最常用)
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); this.age = age; } //继承方法 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); };4.原型式继承
必须有一个对象可以作为另一个对象的基础。
function object(o){ function F(){} F.prototype = o; return new F(); }在object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。从本质上讲,object()对传入其中的对象执行了一次浅复制。ECMAScript 5 通过新增Object.create()方法(等于上边的object)规范化了原型式继承。
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = Object.create(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" //也可以接收两个参数 var anotherPerson = Object.create(person, { name: { value: "Greg" } }); alert(anotherPerson.name); //"Greg"5.寄生式继承
不能做到函数复用。
function createAnother(original){ //这里的object同4中的,object()函数不是必需的;任何能够返回新对象的函数都适用于此模式 var clone = object(original); //通过调用函数创建一个新对象 clone.sayHi = function(){ //以某种方式来增强这个对象 alert("hi"); }; return clone; //返回这个对象 }6.寄生组合式继承
组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); //创建对象 prototype.constructor = subType; //增强对象 subType.prototype = prototype; //指定对象 } 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); this.age = age; } //继承 inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ alert(this.age); };寄生组合式继承是引用类型最理想的继承范式。