标签:
学习资料《JavaScript高级程序设计》
很多Object-Oriented语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,实现继承则继承实际的方法。但是在ECMAScript中函数是没有签名的,所以只支持实现继承,而且实现继承主要是通过原型链来实现的。
1.原型链
基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。实现原型链有一种基本的模式,其代码大致如下:
function SuperType() { this.superproperty=true; } SuperType.prototype.getSuperValue=function () { return this.superproperty; }; function SubType() { this.subproperty=false; } //继承SuperType SubType.prototype=new SuperType(); SubType.prototype.getSubValue=function () { return this.subproperty; }; var instance1=new SubType(); alert(instance1.getSuperValue()); //true
实现继承的本质是重写了原型对象,用一个新类型的实例取代。因此原来存在在SuperType实例中的属性和方法现在也存在于SubType.prototype中了,而且SubType.prototype也定义自己的方法。
instance1指向了SubType的原型,SubType的原型指向了SuperType的原型。注意此时instance1.prototype现在是指向了SuperType,这是因为SubType的原型指向了另一对象SuperType的原型,而SuperType的原型对象的constructor属性是指向SuperType。
实际上是SubType继承了SuperText,2而SuperType继承了Object。
可以采用instanceof操作符或者采用isPrototypeOf()方法判断原型与实例之间的关系。
在给原型添加方法的代码一定要放置在替换原型的语句后面,否则先在原型上定义方法再替换原型那么这些方法都会失效。而且也不可以使用对象自变量的方法创建原型方法。因为这样也相当于重写了原型链。
原型链的问题:
主要是包含引用类型值的原型。在通过原型来实现继承时,原型实际上会变成另外一个类型的实例,那么另外一个类型的实例的属性就会变成subtype的原型的原型属性了。
另外一个主要问题是,在创建子类型的实例时,不能向超类型的构造函数传递参数。
鉴于以上两点原因,实践中很少单独使用原型链。
2.借用构造函数
为了解决原型链中包含引用类型值所带来的问题,开发人员开始使用一种叫做借用构造函数的技术,即在子类型构造函数的内部调用超类型构造函数。例如:
function SuperType() { this.color=[‘red‘,‘black‘,‘green‘]; } function SubType() { //继承了SuperType SuperType.call(this); } var instance1= new SubType(); instance1.color.push("yellow"); alert(instance1.color); //‘red,black,green,yellow‘ var instance2 = new SuperType(); alert(instance2.color); //‘red,black,green‘
通过使用call()方法,实际上是在新创建的SubType实例的环境下调用了SuperType构造函数,因此会在新SubType对象上执行SuperType函数中定义的所有对象初始化代码。
相对于原型链而言,借用构造函数可以在子类型构造函数中向超类型构造函数传递参数。
如果仅仅是借用构造函数的方法,那么就无法避免构造函数模式存在的问题,方法在构造函数中定义,因此函数的复用就无法实现了。而且在超类型的原型中定义的方法,子类型也无法继承。
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.sayAge = function(){ alert(this.age); };
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为JS中常用的继承模式。而且instanceof和isPrototypeOf()方法也能有用于识别组合继承创建的对象。
4. 原型式继承
思想:借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义的类型
function object(o){ function F() {} F.prototype=o; return new F(); }
在object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新的实例。那么这个新的实例的原型就是对象o。
ECMAScript5通过新增Object.create()方法规范了原型式继承。这个方法接受两个参数,一个是用作新对象原型的对象和(可选)一个对新对象定义额外属性的对象。在传入一个参数的情况下,Object.create()与object()方法的行为相同。Obejct.create() 的第二个参数的格式与Object.defineProperties()方法第二个参数的格式相同:每个属性都通过自己的描述符定义。以这种方式指定的任何属性都会覆盖原型对象上的同名属性。
但是,包含引用类型值的属性始终都会共享相应的值。
5.寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,然后再返回对象。
function createAnother(original) { var clone=object(original); //调用object函数创建一个新对象 clone.sayHi=function () { //以某种方式增强这个对象 alert(‘hi‘); } return clone; //返回这个对象 }
使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,这一点与构造函数模式类似。
6.寄生组合式继承
前面说过,组合式继承是JavaScript中最常用的继承模式,但是它也有不足。组合继承的最大问题是调用了两次超类型构造函数。一次是创建子类型的原型的时候,另外一个是在子类型构造函数的内部。在创建子类型的原型的时候,调用超类型构造函数,会将超类型构造函数中的实例属性赋给子类型的原型,但是在调用子类型构造函数的时候,又一个调用了超类型构造函数,会将超类型构造函数中的实力属性赋给子类型构造函数创建的子类型实例,从而这些属性会屏蔽掉子类型原型上的属性。为了解决这个问题,我们采用寄生组合式继承。
所谓寄生组合继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的思路是:不必为了指定子类型的原型而调用超类型的构造函数,所需要的不过就是超类型的一个副本而已,本质上,就是用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
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=Object.create(SuperType.prototype); SubType.prototype.constructor=SubType; SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27
标签:
原文地址:http://www.cnblogs.com/shirleyyang-fe/p/5793732.html