您的位置:首页 > Web前端 > JavaScript

JavaScript中继承(一)-- 原型链

2015-01-11 17:04 274 查看
ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。

构造函数,原型和实例三者间的关系:每个构造函数(Constructor)都有一个原型对象,原型对象(Prototype)都包含一个指向构造函数的指针,而实例(Instance)都包含一个指向原型对象的内部指针。

在默认情况下,我们新创建的JavaScript类的原型对象默认指向Object。但是我们可以让原型对象等于另一个类型的实例。这时,原型对象将包含一个指向另一个原型的指针,相应的另一个原型中也包含着指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上诉关系依然成立,如此就形成了一个递归关系,就构成了实例与原型的链条。这就是所谓的原型链(Prototype
Chian)的基本概念。

实现原型链的一种基本模式:

function SuperType(){
this.property = true;
};

SuperType.prototype.getSuperValue = function(){
return this.property;
};

function SubType(){
this.subProperty = false;
};

SubType.prototype = new SuperType();  // SubType继承SuperType

SubType.prototype.getSubValue = function(){
return this.subProperty;
};

var instance = new SubType();
alert(instance.getSuperVlue());  // >> true;
上面的代码片段定义了两个类型:SuperType和SubType。SubType继承了SuperType,继承是实现方式是:通过创建SuperType的实例,并将该实例赋给SubType.prototype实现的。实现的本质是重写了SubType的原型对象,代之以一个新类型的实例。也即是:原来存在于SuperType的实例中的所有属性和方法,现在也存在于SubType.prototype之中了。在确立了SuperType和SubType的继承关系之后,我们给SubType.prototype添加了一个方法,这样就在继承了SuperType的属性和方法的基础上又添加了一个新方法(方法的添加顺序不同,效果不同!参考后续记录)。

SubType的默认原型被替换成了新的原型(SuperType的一个实例)。新原型不仅具有SuperType的实例所拥有的全部属性和方法。而且内部还有一个指针,指向了SuperType的原型。最终形成了这样一种链式结构:instance的内部属性[[prototype]]指向SubType的原型(SubType Prototype),SubType的原型内部属性[[prototype]]又指向SuperType的原型(SuperType
Prototype)。


需要注意的是:

①:getSuperValue()方法任然还在SuperType.prototype中,但property属性则位于SubType.prototype中(这时因 为property是一个实例属性,而getSuperValue是一个原型方法)。

②:这时的instance.constructor不在指向SubType,这时因为原来的SubType.prototype被重写的缘故。需要我们在此显示的制定这一属性。

上面示例代码中涉及的实例、构造函数和原型之间的关系如下图:



其实上面的这幅图并不是完善的,在JavaScript中所有引用类型默认都继承了Object。而这个继承也是通过原型链实现的。有一点必须记住:“所有函数的默认原型都是Object的实例”。因此默认的原型都会包含一个内部指针指向Object.prototype。下图才是完整的原型链关系图:

第二个需要我们记住的规则:在给原型添加方法的代码一定要放在替换默认原型语句之后。被覆盖的原型会由于没有引用在指向它,而被当成垃圾最终被回收。

第三个需要记住的小规则:在通过原型链实现继承时,不能使用对象字面量创建原型方法。这样做会重写原型链

第四个需要我们知道的问题--原型继承可能存在的隐患

①:在通过原型来实现继承时,原型实际上会变成另一个类的实例。于是,原先的实例属性也就顺理成章的变成了现 在的原型属性了。要知道,原型属性可是会被所有类实例所共享的,如果原型中包含引用类型值,那么将出现我 们意想不到的结果(具体可以参考函数传惨时数值类型和引用类型的区别,都是相同个的原理)。

②:在创建子类型的实例时,不能在不影响所有对象的实例的情况下,向超类型的构造函数传递参数。

针对以上存在的问题,实践中很少会单独使用原型链。进而衍生出来很多不同的继承方式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: