JavaScripts高阶(11)面向对象的继承

188 阅读4分钟

继承方式

很多其他面向对象语言都支持两种继承方式:

  1. 接口继承:只继承方法签名。
  2. 实现继承:继承实际的方法。

由于函数没有签名,ECMAScript只支持实现继承,而且其实现继承主要是通过原型链来实现的

原型链继承:让原型对象指向另一个类的实例

利用原型让一个引用类型继承另一个引用类型的属性和方法

让原型对象指向另一个类的实例,原型对象的__proto__就是指向另一个类的原型,另一个类的原型的constructor就指向了另一个类

示例:SubType.prototype = new SuperType()

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 SubType();
console.log(instance.getSuperValue());  //true

console.log(SubType.prototype);  //{ property: true, getSubValue: [Function] }

instance.getSuperValue()步骤:

  • 1、 instance 的私有属性和私有方法没有 getSuperValue
  • 2、 通过 __proto__找到SubType.prototype的公有属性还是没有getSuperValue
  • 3、 因为SubType.prototype= new SuperType() 所以再次通过 SubType.prototype__proto__找到SuperType.prototype的公有属性getSuperValue
  • 4、 此时SuperType.prototype.getSuperValue中的this是instance
  • 5、 instance 是 SubType的实例所以私有属性和私有方法没有 property
  • 6、 通过 __proto__找到SubType.prototype的公有属性property 输出true

SubType.prototype = new SuperType();原来SuperType的实例中的所有属性和方法(不管是似有还是公有的)现在也存在于SubType.prototype中了

instance通过 __proto__ 指向 SubType.prototype

SubType.prototype通过__proto__ 指向 SuperType.prototype

增加或覆盖方法必须放在替换原型的语句之后

缺点

1、 包含引用类型值的原型属性会被所有势力共享,这也是为什么要在构造函数内而不是在原型对象中定义属性的原因

2、 在创建子类型的实例时,不能向超类型构造函数中传递参数

借用构造函数(call、apply):继承的是私有属性

1、在子类型构造函数内部调用超类型构造函数(要继承的构造函数)

2、将超类型构造函数中的this变为子类型构造函数的实例对象

3、一般用call 、apply 来实现

function SuperType() {
    this.colors = ['red','blue','yellow'];
}

function SubType() {
    //继承了SuperType  
    SuperType.call(this)
    //将SuperType中的this变为SubType的实例对象
}
var instance = new SubType();
instance.colors.push('black');
console.log(instance.colors);   //[ 'red', 'blue', 'yellow', 'black' ]

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

向超类型构造函数中传参:SuperType.call(this,name)

function SuperType(name) {
    this.name = name;
}

function SubType(name) {
    //继承了SuperType
    SuperType.call(this,name)
}
var instance = new SubType('aaa');
console.log(instance.name);   //aaa

缺点

仅仅使用借用构造函数的方法时,并没有继承 超类型构造函数原型上的公用方法,方法都在函数中定义,因此函数复用就无从谈起了

组合继承

使用原型链实现对原型属性和方法(公有属性和方法)的继承,使用借用构造函数来实现对实例私有属性的继承

  1. 使用借用构造函数来实现对实例私有属性的继承:SuperType.call(this,name)
  2. 继承公有属性和方法
  3. 设置constructor保证原型链完整性
function SuperType(name) {
    this.name = name;
    this.colors=['red','blue','yellow']
}
SuperType.prototype.sayName=function () {
    console.log(this.name);
}
function SubType(name,age) {
    //1、继承 私有属性
    SuperType.call(this,name)
    this.age=age;
}
//2、继承公有属性和方法
SubType.prototype=new SuperType();
//3、设置constructor保证原型链完整性
SubType.prototype.constructor=SubType;
//新增方法
SubType.prototype.sayAge=function () {
    console.log(this.age);
}

var instance1 = new SubType('aaa',18);
instance1.colors.push('black')
console.log(instance1.colors);      //[ 'red', 'blue', 'yellow', 'black' ]
console.log(instance1.name);        //aaa
instance1.sayName();                //aaa
instance1.sayAge();                 //18

var instance2 = new SubType('bbb',16);
console.log(instance2.colors);      //[ 'red', 'blue', 'yellow' ]
console.log(instance2.name);        //bbb
instance2.sayName();                //bbb
instance2.sayAge();                 //16

缺点:多次调用超类型构造函数导致效率下降

寄生组合式继承

寄生式继承:创建一个仅用于封装继承过程的函数

该函数在内部以某种方式增强对象(设置constructor、新增方法等),最后再像真的是他做了所有工作一样返回对象

寄生组合式继承:

  1. 通过借用构造函数来继承属性,
  2. 通过原型链的混成形式方法继承方法,
  3. 使用寄生式继承来继承超类型的原型

基本思路:不必为了指定子类型的原型而调用超类型够造函数,只需要超类型原型的一个副本

本质:使用寄生式继承来继承超类型的原型,然后再将结果指定给值类型的原型

基本模块

//继承方法的函数
function inheritPrototype(subType,superType) {
    //创建对象
    var prototype = Object(superType.prototype);
    //增强对象
    prototype.constructor=subType;
    //指定对象
    subType.prototype=prototype;
}

寄生组合式继承成品

//继承方法的函数
function inheritPrototype(subType,superType) {
    //创建对象
    var prototype = Object(superType.prototype);
    //增强对象
    prototype.constructor=subType;
    //指定对象
    subType.prototype=prototype;
}

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

function SubType(name,age) {
    //继承 属性 (1)
    SuperType.call(this,name)
    this.age=age;
}
//调用继承方法  (2)
inheritPrototype(SubType,SuperType);
//新增方法
SubType.prototype.sayAge=function () {
    console.log(this.age);
}

var instance1 = new SubType('aaa',18);
instance1.colors.push('black')
console.log(instance1.colors);      //[ 'red', 'blue', 'yellow', 'black' ]
console.log(instance1.name);        //aaa
instance1.sayName();                //aaa
instance1.sayAge();                 //18

var instance2 = new SubType('bbb',16);
console.log(instance2.colors);      //[ 'red', 'blue', 'yellow' ]
console.log(instance2.name);        //bbb
instance2.sayName();                //bbb
instance2.sayAge();                 //16