Javascript之继承(原型链方式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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 Son(); alert(instance.getSuperValue()); //true alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true |
以上代码定义了两个类型:SuperType和SubType。每个类型分别有一个属性和方法。它们主要区别就是SubType继承了SuperType,而这继承是通过创建SuperType新实例,并将这个新实例赋给SubType.prototype实现的。实现的本质就是重写原型对象,代之以新类型的实例。
instance指向Subtyoe.prototype,SubType.prototype又指向了SuperType.prototype。getSuperValue()方法仍然还在SuperType。prototype中,但property则位于SubType.prototype中。这是因为property是一个实例属性,而getSyoerValue()则是一个原型方法。既然SubType.prototype现在是SuperType的实例,那么property当然位于该实例中。此外,要注意instance.constrcutor现在指向的是SuperType,这是因为原来SubType.propertype中的constructor被重写了的缘故。
注:SubType.propertype中的constructor不被重写了的缘故,而是SubType的原型指向了另一个对象——SuperType的原型,而这个原型对象的constructor是指向SuperType的。
通过实现原型链,本质上就是扩展了原型搜索机制。
2.别忘了默认的原型
事实上,我们的原型链还少了一环,所有引用类型默认继承了Object类型,而这个继承也是通过原型链实现的。我们要记住,所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype。这也正式自定义类型能使用toString()等默认方法的原因。
一句话,SubType集成了SuperType,而SuperType继承了Object。当调用instance.toSring()时,实际调用的是保存在Object.prototype中的那个方法。
3.确定原型和实例的关系
可以使用instanceof操作符来测试实例与原型链中出点过的构造函数。
1 2 3 | alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true |
4.谨慎的定义方法
子类型有时需要重写父类型中的某个方法,或者需要添加父类型中不穿在的某个方法,但不管怎么,给原型添加的代码一定要放在替换原型的语句之后。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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; } //重写父类型中的方法 SubType.prototype.getSuperValue= function (){ return this .subproperty; } var instance= new SubType(); alert(instance.getSuperValue()); //false |
注意,在通过原型链实现继承时,不能使用对象字面量创建原型对象,这样会重写原型链。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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; }, someOtherMethod: function (){ return false ; } } var instance= new SubType(); alert(instance.getSuperValue()); //error |
由于现在的原型包含的是一个Object的实例,而非SuperType的实例,因此我们设想中的原型链已经别切断——SubType和SuperType已经没关系了。
5.原型链的问题
原型链最主要的问题还是来支援引用类型的原型,我们以前说过包含引用类型值的原型属性会被所有实例共享,这也是为什么要在构造函数中定义属性,而不是在原型中定义属性的原因了。在通过原型来实现继承时,原型实际上会被变成另一个类型的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function SuperType(){ this .colors=[ "red" , "blue" , "green" ]; } function SubType(){ } //继承了SuperType SubType.prototype= new SuperType(); var instance= new SubType(); instance.colors.push( "black" ); //"red","blue","green","black" alert(instance.colors); var instance2= new SubType(); alert(instance2.colors); //"red","blue","green","black" |
原型的第二个问题:在创建子类的实例时,不能向父类型的构造函数草地参数,所以实践中很少单独用到原型链。