继承

ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链来实现的。

原型链

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。具体来说就是让原型对象和实例之间实现等价关系,这样,通过实例指向原型对象,让一个原型对象指向了另一个原型对象,实现层层继承的关系。

function SuperValue(){
            this.property = true;
       }
       SuperValue.prototype.getSuperValue = function(){
            return this.property;
       }
       function SubValue(){
            this.subProperty = false;
       }
       SubValue.prototype = new superValue();//继承superValue,修改SubValue的原型
       SubValue.prototype.getSubValue = function(){
            return this.subProperty;
       }//添加getSubValue 方法
       var subObj = new SubValue();//创建subValue的实例
       alert(subObj.getSubValue());//false
       var superObj = new SuperValue();//创建superValue的实例
       alert(superObj.getSubValue());//error

在上例中,SubValue的默认原型未被使用,而是给它换了一个新原型,新原型不仅具有作为一个SuperType 的实例所拥有的全部属性和方法,而且其内部还有一个指针,指向了SuperType 的原型。修改新原型的属性不会影响到SuperValue原型。现在,Subvalue原型的constructor指向了SuperValue。

所有函数的默认原型都是Object 的实例,因此,默认原型都会包含一个内部指针,指向Object.prototype。

谨慎定义原型

给原型添加方法的代码一定要放在替换原型的语句之后。

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的实例调用,返回false,但是SuperType的实例访问此方法时,返回的仍是true。
SubType.prototype.getSuperValue = function (){
  return false;
};
var instance = new SubType();
alert(instance.getSuperValue()); //false

在通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做就会重写原型链,从而无法实现继承。

原型链的问题
问题一:

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的实例都会共享color属性,所以,在instance1中修改属性的值在instance2中也会反应出来。

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

解决办法:

借用构造函数继承原型

在子类型构造函数的内部调用超类型构造函数,通过使用apply()和call()方法也可以在(将来)新创建的对象上执行构造函数。

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,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"

上例的意思是会在新SubType 对象上执行SuperType()函数中定义的所有对象初始化代码。结果,SubType 的每个实例就都会具有自己的colors 属性的副本。

构造函数的另一个好处是:可以在子类型构造函数中向超类型构造函数传递参数。缺点是:方法在函数内部定义,无法复用。

组合继承

其思想是:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。

function SuperType(name){
        this.colors = ['blue','red','green']; 
        this.name = name;
       }
       SuperType.prototype.sayName = function(){
        alert(this.name);
       }
       function SubType(name,age){
        SuperType.call(this);
        //继承属性
        this.name = name;
        this.age =  age;
       }
       //继承方法
       SubType.prototype = new SuperType();
       SubType.prototype.constructor = SubType;
       SubType.prototype.sayAge = function(){
        alert(this.age);
       }
       var instance1 = new SubType('Tom',28);
       instance1.colors.push('black');
       alert(instance1.colors);//"red,blue,green,black"
       instance1.sayName();'Tom'
       instance1.sayAge();//29
       var instance2 = new SubType('Jake',25);
       alert(instance2.colors);//"red,blue,green"
       instance2.sayName();//'Jake'
       instance2.sayAge();//25

这样一来,就可以让两个不同的SubType 实例既分别拥有自己属性——包括colors 属性,又可以使用相同的方法了。这是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); //第二次调用SuperType()
    this.age = age;
}
SubType.prototype = new SuperType(); //第一次调用SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
};

所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。

寄生组合式继承的基本模式:

function inheritPrototype(SubType, SuperType){
    var prototype = object(superType.prototype); //创建对象
    prototype.constructor = subType; //增强对象
    subType.prototype = prototype; //指定对象
}

这个函数接收两个参数:子类型构造函数和超类型构造函数。在函数内部,第一步是创建超类型原型的一个副本。第二步是为创建的副本添加constructor 属性,从而弥补因重写原型而失去的默认的constructor 属性。最后一步,将新创建的对象(即副本)赋值给子类型的原型。

function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
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;
}
inheritPrototype(SubType, SuperType);//寄生组合式继承
SubType.prototype.sayAge = function(){
    alert(this.age);
};
var instance1 = new SubType('Tom',24);
instance1.colors.push('black');
instance1.sayName();//'Tom'
instance1.sayAge();//24
alert(instance1.colors);
var instance2 = new SubType('Jake',25);
alert(instance2.colors);
instance2.sayName();//'Jake'
instance2.sayAge();//25

寄生组合式继承是引用类型最理想的继承范式。


阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: JavaScriptJava