许多oo语言支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。而函数没有签名,在ECMAScript中无法实现接口继承,只支持实现继承,主要依靠原型链来实现继承。
1、子类的原型对象-类式继承
//类式继承 //声明父类 function SuperClass(){ this.superValue = true; } //为父类添加共有方法 SuperClass.prototype.getSuperValue=function(){ return this.supervalue; }; //声明子类 function SubClass(){ this.subVlaue=false; } //继承父类 SubClass.prototype=new SuperClass();
SubClass.prototype.contructor=SubClass; //为子类添加共有方法 SubClass.prototype.getSubValue=function(){ return this.subValue; }
类式传承需要将第一个类的实例赋值给第二个类的原型。类的原型对象的作用就是为原型添加共有方法,但类不能直接访问这些属性和方法,必须通过原型prototype来访问 。但是要注意的是SubClass的原型的constructor属性被重写了,SubClass的原型指向另一个对象的原型,而这个对象的constructor属性指向的是SubClass.
通过原型链实现继承的时候,不能使用对象字面量创建原型方法,这样会重写原型链。
//继承父类 SubClass.prototype=new SuperClass(); //使用字面量添加方法会导致上面代码失效 SubClass.prototype={ getSubValue:function(){ return this.subValue; }
原型链也存在一些问题,一是原型中包含引用类型值所带来的问题,一个子类的实例更改原型从父类构造函数中继承来的共有属性就会直接影响其他子类,引用类型值的原型属性会被所有的实例共享,这也是为什么在构造函数中定义属性而不是在原型对象定义属性的原因。二是在创建子类型的实例时,不能向超类型的构造函数中传递参数,实际上是没办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。因此实践中很少使用原型链。
2.构造函数继承
为解决引用类型值带来的问题,使用一种借用构造函数的技术,即通过apply()和call()方法在子类构造函数的内部调用超类型构造函数
//构造函数继承 //声明父类 function SuperClass(id){ //引用类型共有属性 this.books=['Javascript', 'html', 'css']; // 值类型共有属性 this.id=id; } //父类声明原型方法 SuperClass.prototypw.showBooks=function(){ console.log(this.books); } //声明子类 function SubClass(id){ //继承父类 SuperClass.call(this, id); }
通过call在子类的构造函数作用环境中执行一次父类的构造函数,子类中会具有自己属性的副本了,同时还能传递参数。
构造函数也存在一些问题,方法在构造函数中定义,函数复用就无从谈起了,,而且超类原型中定义的方法,对子类而言是不可见的。
3.组合继承
组合继承将原型链和借用构造函数的技术组合到一块
//组合式继承 //声明父类 function SuperClass(name){ //引用类型共有属性 this.books=['Javascript', 'html', 'css']; // 值类型共有属性 this.name=name; } //父类声明原型方法 SuperClass.prototypw.getName=function(){ console.log(this.name); } //声明子类 function SubClass(name,time){ //构造函数继承父类 SuperClass.call(this, name; //子类型增共有属性 this.time=time; } //类式继承 子类原型继承父类 SubClass.prototype=new SuperClass(); //子类原型方法 SubClass.prototype.getTime=function(){ console.log(this.name); };
这种方式父类构造函数调用了两遍,所以不是完美的继承方式
4.原型式继承
原型式继承可以不必在预先定义构造函数的情况下实现继承,其本质是执行对给定对象的潜复制,而复制得到的副本还可以进一步改造,要求有一个对象作为另一个对象的基础,新对象将传入的对象o作为原型
function object(o){ function F(){} F.prototype = o; return new F(); }
ECMAscript5新增Object.create()方法与object方法的行为相同
5.寄生式继承
寄生式继承与原型继承非常相似,也是基于某个对象或某些信息创建一个对象,然后增强对象,最后返回对象。
function creatAnother(original){ var clone =object(original); //调用一个函数创建一个新对象 clone.sayHi=function(){ //以某种方式增强这个对象 alert("hi"); }; return clone; //返回这个对象 }
6.寄生组合式继承
寄生式继承,集寄生和组合继承的优点与一身,是实习基于类型继承的最有效的方法
function inheritPrototype(subClass,superClass){ //复制一份父类的原型副本保存在变量中 var p=object(superClass.prototype); //修正因重写子类型原型导致子类的constructor属性被修改 p.constructor=subClass; //设置子类的原型 subClass.prototype=p; }
子类型再想添加原型方法必须通过prototype对象,通过点语法的形式一个一个添加方法,否则对象就会覆盖掉从父类原型继承的对象了
// 定义父类 funcrion SuperClass(name){ this.name=name; this.color = ['red', 'blue', 'green']; } //定义父类原型方法 SuperClass.prototype.getName=function(){ console.log(this.name); }; //定义子类 function SubClass (name,time){ //构造函数式继承 SuperClass.call(this,name); //子类新增属性 this.time = time; } //寄生式继承父类原型 inheritPrototype(Subclass,SuperClass); //子类新增原型方法 SubClass.prototype.getTime()=function(){ console.log(this.time); };