js继承

Js中继承主要是依靠原型链来实现的。每个构造函数都有一个原型对象prototype,原型对象都包含一个指向构造函数的指针constructor,而所有实例都包含一个指向原型对象的内部指针[[prototype]]。

一、原型链

原型链的基本概念:假设A的原型对象等于另一个类型B的实例,则A的原型对象中将包含一个指向B的原型对象的指针。相应的,如果B的的原型对象等于另一个类型C的实例,则B的原型对象中将包含一个指向C的原型对象的指针。如此层层递进,就构成了实例与原型的链条。

原型链的基本模式如下:

function SuperType(){
    this.prototype = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.prototype;
}
function SubType(){
    this.subprptotype = false;
}
// subType继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
    return this.subprototype;
}
var instance = new SubType();
alert(instance.getSuperValue());   //true;
将SubType的原型对象定义为SuperType的实例后,原来存在于SuperType的实例中的所有属性和方法,都存在与SubType.prototype中了。这个例子中的实例以及构造函数和原型之间的关系如下图:

当以读取模式访问一个实例属性时,首先再 实例中搜索该属性,如果没有找到该属性,则会继续搜索实例的原型。在实例原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。在上述例子中,搜索instance.getSuperTypeValue()会经历三个搜索步骤:1)搜索实例;2)搜索SubType.prototype;3)搜索SuperType.prototype。

在通过原型链实现继承时,不能使用对象字面量创建原型方法,因为对象字面量会重写原型链,使已有的原型链被切断。

用原型链实现继承与原型模式创建对象具有一样的问题:包含引用类型值的属性会被所有实例共享。如下面这个例子:

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'
当SubType通过原型链继承了SuperType后,SubType.prototype就变成了SuperType的一个实例,自然也就拥有了colors属性。因此SubType的所有实例都会共享这一个colors属性。在上例中,对instance1.colors的修改能够通过instance2.colors反应出来。

原型链的第二个问题,是在创建子类型实例时,不能向超类型的构造函数中传递参数。

二、借用构造函数

为了解决原型中包含引用类型值所带来的的问题,出现了借用构造函数的继承方法,即使用call()或者apply()方法,在子类型构造函数的内部调用超类型构造函数。并且,借用构造函数的继承方法还可以在子类型构造函数中向超类型构造函数传递参数。如下例所示:

function SuperType(name){
    this.colors = ['red','blue','green'];
    this.name = name;
}
function SubType(){
    //继承了SuperType,而且还传递了参数
    SuperType.call(this,'Nicholas');
    //实例属性
    this.age = 29;
}
var instance1 = new SubType();
instance1.colors.push('balck');
alert(instance1.colors);  //'red,blue,green,black'
var instance2 = new SubType();
alert(instance2.colors);   //'red,blue,green'
alert(instance1.name);   //Nicholas
alert(instance1.age);   //29
借用构造函数存在的问题与使用构造函数常见对象一样,无法做到函数复用,即每个方法都要在每个实例中创建一遍。

三、组合继承

使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既可以在原型上定义方法实现函数复用,有能够保证每个实例都有自己的属性,避免应用类型值的属性被所有实例共享的问题。

function SuperType(name){
    this.colors = ['red','blue','green'];
    this.name = name;
}
SuperType.prototype.sayName = function(){
    alert(this.name);
}
function SubType(name,age){
    //继承属性
    SuperType.call(this,name);   //第二次调用SuperType()
    this.age = age;
}
//继承方法
SubType.prototype = new SuperType();   //第一次调用SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
}
var instance1 = new SubType('Nicholas',29);
instance1.colors.push('balck');
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'
instance1.sayName();   //'Greg'
instance1.sayAge();   //29
组成继承的问题在于必须调用两次超类型的构造函数,一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。在上例中,第一次调用SuperType构造函数后,SubType.prototype得到两个属性:name和colors,他们都是SuperType的实例属性。当调用SubType构造函数时,会再一次调用SuperType构造函数,这一次又在新对象上创建了两个实例属性name和colos。因此,这两个属性就屏蔽了原型中的两个同名属性。

四、寄生组合式继承

所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式开来继承方法,也就是使用寄生式继承来继承超类型的原型,然后将结果指定给子类型的原型。

寄生组合类型最基本的模式如下,其中object()函数为任何能够返回新对象的函数:

function inheritPrototype(subType,superType){
    var prototype = object(superType.prototype);
    prototype.constructor = subType;
    subType.prorotype = prototype;
}
因此,组合继承中的实例便可以修改为:

function SuperType(name){
    this.colors = ['red','blue','green'];
    this.name = name;
}
SuperType.prototype.sayName = function(){
    alert(this.name);
}
function SubType(name,age){
    //继承属性
    SuperType.call(this,name);   
    this.age = age;
}
//继承方法
inheritPrototype(SubType,SuperType);   
SubType.prototype.sayAge = function(){
    alert(this.age);
}

寄生组合式继承的高效率体现在只需要调用一次超类型的构造函数,并且避免了在子类型的prototype对象上创建不必要的、多余的属性,与此同时,还能保持原型链不变。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值