虽然在ES6中有了继承,使用extends关键字就能实现。本篇讲的不是这种,而是ES6以前的几种实现继承的方式。app
(一)原型链函数
ECMAScript中将原型链做为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。(不理解原型链的童鞋们能够翻阅一下我以前的博客,里面有详细的说明)this
实现原型链的一种基本模式spa
function SuperType(){ this.prototype=true; } SuperType.prototype.getSuperValue=function(){ return this.property; } function SubType(){ this.subproperty=false; } //经过建立SuperType的实例继承了SuperType SubType.prototype=new SuperType(); SubType.prototype.getSubValue=function(){ return this.subproperty; } var instance=new SubType(); alert(instance.getSuperValue()); //true
上面的例子中,instance指向SubType的原型,SubType的原型又指向SuperType的原型。getSuperValue()方法仍然还在SuperType.prototype中,但property则位于SubType.prototype中。这是由于prototype是一个实例属性,而getSuperValue()则是一个原型方法。prototype
注意:原型链虽然很强大,能够实现继承,但存在两个主要的问题。(1)包含引用类型值的原型属性会被全部实例共享,这会致使对一个实例的修改会影响另外一个实例。code
(2)在建立子类型的实例时,不能向超类型的构造函数中传递参数。因为这两个问题的存在,实践中不多单独使用原型链。对象
如下例子清楚的说明了第一个问题blog
function SuperType(){ this.colors=["red", "blue", "green"]; } function SubType(){ } //继承了SuperType SubType.prototype=new SuperType(); 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,black
(二)借用构造函数继承
在解决原型中包含引用类型值所带来的问题中,使用借用构造函数技术来解决。借用构造函数的基本思想,即在子类型构造函数的内部调用超类型构造函数。函数只不过是在特定环境中执行代码的对象,所以经过使用apply()和call()方法能够在新建立的对象上执行构造函数。以下例子ip
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,bllue,green,black var instance2=new SubType(); alert(instance2.colors); //red,blue,green
上面例子中,经过使用call()方法(或者apply()方法),在新建立的SubType实例的环境下调用了SuperType构造函数。这样一来就会在新的SubType对象上执行SuperType()函数中定义的全部对象初始化代码。结果,SubType的每一个实例都会有本身的colors属性副本。
相对于原型链而言,借用构造函数能够在子类型构造函数中向超类型构造函数传递参数。以下例子
function SuperType(name){ this.name=name; } function SubType(){ //继承了SuperType,同时还传递了参数 SuperType.call(this,"mary"); //实例属性 this.age=22; } var instance=new SubType(); alert(instance.name); //mary alert(instance.age); //29
借用构造函数存在两个问题:(1)没法避免构造函数模式存在的问题,方法都在构造函数中定义,所以没法服用函数。(2)在超类型的原型中定义的方法,对子类型而言是不可见的。所以这种技术不多单独使用。
(三)组合继承
组合继承,指的是将原型链和借用构造函数的技术组合到一块儿。思路是使用原型链实现对原型方法的继承,而经过借用构造函数来实现对实例属性的继承。这样,既经过在原型上定义方法实现了函数的复用,又可以保证每一个实例都有它本身的属性。如下例子充分说明了这一点
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); }; var instance1=new SubType("mary", 22); instance1.colors.push("black"); alert(instance1.colors); //red,blue,green,black instance1.sayName(); //mary instance1.sayAge(); //22 var instance2=new SubType("greg", 25); alert(instance2.colors); //red,blue,green instance2.sayName(); //greg instance2.sayAge(); //25
这个例子中,两个实例既分别拥有本身的属性,包括colors属性,又能够使用相同的方法。
组合继承避免了原型链和借用构造函数的缺点,融合了他们的优势,是JavaScript中最经常使用的继承模式。