JavaScript中实现继承

  在JavaScript中实现继承主要实现以下两方面的属性和方法的继承,这两方面相互互补,既有共享的属性和方法,又有特有的属性和方法。

  1. 实例属性和方法的继承:
    目的:每个实例都有自己特有的属性和方法。特别是引用类型属性,如果被共享,所有实例都可修改引用类型属性,并且反应到所有实例中。
  2. 原型属性和方法的继承:
    目的:在继承中实现属性和方法的共享。避免每创建一次实例,都要新建一次属性和方法。

ES5—寄生组合式继承

  ES5的继承方式有多种:主要有原型链继承、借用构造函数、组合式继承、寄生式继承和寄生组合式继承。寄生组合式继承集组合式继承和寄生式继承的优点于一身,是ES5中,基于类型继承的最有效方式。
  接下来基于寄生组合式继承对ES5实现继承的方面进行解释。

//父类
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);//1.借用构造函数:继承父类的实例属性;
    this.age = age;
}
//2.寄生式继承:将父类原型的副本强制赋值给子类原型,实现继承父类的原型方法。
inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function(){
    alert(this.age);
};

function inheritPrototype(subType, superType){
    var prototype = object(superType.prototype); //创建父类原型的副本
    prototype.constructor = subType; //将该副本的constructor属性指向子类
    subType.prototype = prototype; //将子类的原型属性指向副本
}

  以上示例就是ES5中寄生组合式继承的一个例子,如何实现子类继承父类?

  • 借用构造函数SuperType.call(this, name);:当new SubType()创建子类实例时,首先调用父类构造函数,实现了子类实例继承父类的实例属性和方法
  • 寄生式继承inheritPrototype(SubType, SuperType);:将父类原型副本强制替换成子类原型(1.副本constructor指向子类;2.子类prototype指向副本),使得子类原型包含父类原型中的所有属性和方法,实现了原型属性和方法的继承。

ES6中Class继承

  ES5中通过函数创建类型并基于原型实现继承的方式与其他面向对象的语言相比确实比较另类,没有那么简单明了;ES6就提供了更加接近传统语言的写法,引入了类的概念。

通过class关键字定义类

class super{
    constructor(name,color){
        this.name=name;
        this.color=["red","blue","green"];
    }
    sayName(){
        alert(this.name);
    }
}
 1. constructor为构造函数,如果非显示创建构造函数,定义类时也会自动创建构造函数;
 2. 通过`this`定义的属性和方法属于实例属性和方法;否则都是定义在原型上的属性和方法;
 3. class类中定义的方法`constructor、sayName`都属于原型方法。

extends实现类的继承

class Person{
    constructor(name,color){
        this.name=name;
        this.color=["red","blue","green"];
    }
    sayName(){
        alert(this.name);
    }
}

class Student extends Person(){
    constructor(name,color,score){
        super(name,color);//调用父类构造函数,this指向子类
        this.score=score;
    }
    showScore(){
        alert(this.score);
    }
}

let stu1=new Student("xuxu",["white","black","pink"],90);
stu1.sayName();//"xuxu"
stu1.showScore();//90
 1. 子类构造函数中调用父类构造函数,实现了子类继承父类的实例属性和方法;
 2. 通过extends,子类原型继承父类原型上的属性和方法:
    - Student.__proto__=Person;//作为对象,子类原型等于父类(构造函数的继承)
    - Student.prototype.__proto__=Person.prototype;//作为构造函数,子类原型对象是父类原型对象的实例。
 3. 子类静态方法继承父类静态方法
    - 静态方法的定义:关键字static;
    - 调用方法:类名调用(而不是实例调用);
    - 普通方法中,super作对象表示父类原型(用来调用父类原型方法);
      静态方法中super作对象表示父类(用来调用父类静态方法)。
    - 普通方法中this指向实例对象;静态方法中hits指向当前子类。

prototype和__proto__相互关系

对象(实例和原型对象)有__proto__属性,构造函数有prototype属性,原型对象有constructor属性。
  假设class B extends A,实例对象分别是insB、insA:

  1. A.prototype.constructor=A;//类的原型的构造函数指向类本身。
  2. insA.__proto__=A.prototype;//类的实例的原型指向创建它的构造函数的原型。
  3. B.__proto__=A;//作为对象,子类的原型等于父类
  4. B.prototype.__proto__=A.prototype;//作为构造函数子类原型继承父类原型
  5. insB.__proto__.__proto__=insA.__proto__;//子类实例的原型的原型指向父类实例的原型

总结

  1. ES5中:

    • 利用借用构造函数实现 实例属性和方法的继承
    • 利用原型链或者寄生式继承实现 共享的原型属性和方法的继承
  2. ES6中:

    • 利用class定义类,extends实现类的继承;
    • 子类constructor里调用super()(父类构造函数)实现 实例属性和方法的继承
    • 子类原型继承父类原型,实现 原型对象上方法的继承
03-05 16:53