继承实现方式

一、原型链继承

将父类的实例作为子类的原型

function Parent(){
        this.name='lily';
        
        this.info={
            score:99,
            color:"red"
        }
    }
    Parent.prototype.getMessage = function(){
        console.log(this.name,this.info)
    }
    function Child(){
        
    }
    Child.prototype = new Parent();
    let child1 = new Child('lily',18,'女');
    
    child1.info.score = 100
    child1.getMessage();
    let child2 = new Child();
    child2.getMessage();
优点:

父类方法可以复用

缺点:

父类的所有引用属性(info)会被所有子类共享,更改一个子类的引用属性,其他子类也会受影响
子类型实例不能给父类型构造函数传参

二、构造函数继承

在子类构造函数中调用父类构造函数,可以在子类构造函数中使用call()和apply()方法

function Parent(name){
        this.name=name;
        this.info={
            age:18,
            score:88,
        }
    }
    Parent.prototype.getMessage=function(){
        console.log(`${this.name}在跑`);
    }
    function Child(name,sex){
        Parent.call(this,name);
        this.sex=sex;
    }
    let child1 = new Child('zhangsan',18);
    child1.info.age=88;
    // child1.getMessage()会报错 Uncaught TypeError: child1.getMessage is not a function

    console.log(child1)
    let child2 = new Child('李四',16);
    console.log(child2)
优点:

可以在子类构造函数中向父类传参数
父类的引用属性不会被共享

缺点:

子类不能访问父类原型上定义的方法(即不能访问Parent.prototype上定义的方法),因此所有方法属性都写在构造函数中,每次创建实例都会初始化

三、Class继承:子类通过extends来继承父类,通过super来调用父类的构造函数,super必须放在子类this之前.

可以调用父类的属性和方法

class Person{
      constructor(name) {
          this.name=name
      }
      run(){
          console.log(`${this.name}在跑`)
      }
  }
  class Student extends Person{
      constructor(name,score) {
          super(name)
          this.score=score
      }
      study(){
          console.log(`${this.name}考了${this.score}分`);
      }
  }
  let student = new Student('小明',98);
  console.log(student)
  student.study()
  student.run()

四、组合继承

组合继承综合了原型链继承和盗用构造函数继承(构造函数继承),将两者的优点结合了起来,
基本的思路就是使用原型链继承原型上的属性和方法,而通过构造函数继承实例属性,这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性
function Parent(name){
        this.name= name||'gy'
        this.colors = ['red','pink','gold']
    }
    Parent.prototype.drink = function(){
        console.log(`${this.name}在喝水`)
    }
    function Child(name,sex){
        // 继承父类的属性
        Parent.apply(this,[name]);
        // 拥有自己的属性
        this.sex=sex;
    }
    // 继承父类的方法
    Child.prototype = new Parent();
    Child.prototype.run = function(){
        console.log(`${this.name}在跑`);
    }
    let child1 = new Child('lucy','女');
    child1.colors.push("blue")//数组是父类原型上的引用数据类型
    console.log(child1);
    child1.drink();
    
    let child2 = new Child('Blank','男');
    
    console.log(child2);
    child2.drink();

 

上面例子中,Parent构造函数定义了name,colors两个属性,接着又在他的原型上添加了个drink ()方法。Child构造函数内部调用了Parent构造函数,同时传入了name参数,同时Child.prototype也被赋值为Parent实例,然后又在他的原型上添加了个run ()方法。这样就可以创建 child1,child2两个实例,让这两个实例都有自己的属性,包括colors,同时还共享了父类的drink方法

优点

父类的方法可以复用
可以在Child构造函数中向Parent构造函数中传参
父类构造函数中的引用属性不会被共享





发表评论 / Comment

用心评论~