[JavaScript高级程序设计]面向对象设计(3)--继承

继承

许多OOP语言都支持两种继承方式:接口继承和实现继承。由于ECMAScript中函数没有签名,故无法实现接口继承,只支持实现继承。ECMAScript中的的继承主要是通过原型链实现的。

1 原型链

利用原型让一个引用类型继承另一个引用类型的属性和方法。当我们让一个引用类型的原型等于另一个引用类型的实例,那么该引用类型的远新对象中将包含一个指向另一个原型的指针。以此可以构成原型链。

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

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

var instance = new SubType();
console.log(instance.getSuperTypeValue());//true

1.1 别忘了默认的原型

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

1.2 确定原型和实例之间的关系

第一种方法:使用instanceof操作符。

console.log(instance instanceof SuperType);    //true
console.log(instance instanceof SubType);      //true
console.log(instance instanceof Object);       //true

第二种方法:使用isPrototypeOf()方法。

console.log(SubType.prototype.isPrototypeOf(instance));      //true
console.log(SuperType.prototype.isPrototypeOf(instance));    //true
console.log(Object.prototype.isPrototypeOf(instance));       //true

1.3 谨慎地定义方法

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

2 借用构造函数

有时候也叫伪造对象或经典继承。基本思想:在子类型构造函数的内部调用超类型构造函数。(函数只不过是在特定环境中执行代码的对象,可以使用call()或apply()在心间的对象上执行构造函数。

function SuperType(name){
    this.name = name;
}
function SubType(name,age){
    SuperType.call(this,name);
    this.age = age;
}
var instance = new SubType('Nicholas',29);
console.log(instance.name);    //'Nicholas'

3 组合继承

也叫伪经典继承。指的是将原型和构造函数的方式结合在一起,从而发挥二者之长的一种模式。即使用原型链实现对原型属性和方法的继承,而通过构造函数实现实例属性的继承。

function SuperType(name){
    this.name = name;
    this.colors = ['red','blue','green'];
}
SuperType.prototype.sayName = function(){
    console.log(this.name);
}

function SubType(name,age){
    SuperType.call(this,name,age);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
    console.log(this.age);
}

var instance1 = new Subtype('Nicholas',29);
instance1.colors.push('black');
console.log(instance1.colors);    //['red','blue','green','black']

var instance2 = new SubType('Gred',26);
console.log(instance2.colors);    //['red','blue','green']

4 原型式继承

即借助原型可以基于已有的对象创建新的对象,同时还不必因此创建自定义类型。在ECMAScript5中给出Object.create()方法实现,这个方法接受两个参数:一个用作新对象原型的对象,一个(可选的)为新对象定义的额外属性的对象。

var person = {
    name:'Nicholas',
    friends:['Cindy','Lily'];
}
var anotherPerson = Object.create(person,{
    name:{
        value:'Gred'
    }
});

console.log(anotherPerson.name);    //'Gred'

其中,Object.creat()方法的原理为:

function createObject(o){
    function F(){};
    F.prototype = o;
    return new F();
}

5 寄生式继承

思路与工厂模式类似,即创建一个仅用于封装过程的函数,该函数在内部以某种方式来增强对象,最后再像真的做了所有工作一样返回对象。

function createAnother(original){
    var clone = Object.create(original);
    clone.sayHi = function(){
        alert("hi");
    }
    return clone;
}

6 寄生组合式继承

组合继承最大的问题就是:无论在什么情况下,都会调用两次超类型构造函数。一次是在创建子类型的远行的时候,另一次是在子类型狗在函数内部。并且,在子类型原型的原型中有和实例属性重复的属性。

function SuperType(name){
    this.name = name;
    this.colors = ['red','blue','green'];
}
SuperType.prototype.sayName = function(){
    console.log(this.name);
}

function SubType(name,age){
    SuperType.call(this,name,age);          /*第二次调用SuperType()*/
    this.age = age;
}
SubType.prototype = new SuperType();        /*第一次调用SuperType()*/
SubType.prototype.sayAge = function(){
    console.log(this.age);
}

var instance1 = new Subtype('Nicholas',29);
instance1.colors.push('black');
console.log(instance1.colors);    //['red','blue','green','black']

var instance2 = new SubType('Gred',26);
console.log(instance2.colors);    //['red','blue','green']

解决方法:寄生组合继承

寄生组合继承就是通过借用构造函数来继承属性,通过原型链的混合形式来继承方法,基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们需要的无非就是超类型原型的一个副本而已。对于上述代码修改一句,如下:

SubType.prototype = Object.create(SuperType.prototype);
SubType.constructor = SubType;

猜你喜欢

转载自blog.csdn.net/Liwen_Ye/article/details/81415854