js中的继承

347 阅读3分钟

继承

继承:子类继承父类中的属性和方法「目的是让子类的实例能够调取父类中的属性和方法」

1.原型继承

让父类中的属性和方法在子类实例的原型链上;

  • Child.prototype=new Parent();
  • Child.prototype.constructor=Child;

原型继承

function Parent(){
    this.names=['Jack','Tom'];
}

Parent.prototype.getName=function(index){
    return this.names[index];
}

function Child(){}

//原型链继承
Child.prototype=new Parent();
Child.prototype.constructor=Child;

const child1=new Child();
child1.names.push('Lucy');
console.log(child1.getName(0));//Jack
console.log(child1.names);//[ 'Jack', 'Tom', 'Lucy' ]

const child2=new Child();
console.log(child2.getName(0));//Jack
console.log(child2.names);//[ 'Jack', 'Tom', 'Lucy' ]
  • 特点:

    • 1.不像其他语言中的继承一样「其他语言的继承一般是拷贝继承」,它是把父类的原型放到子类实例的原型链上,实例想调取这个方法,是基于__proto__原型链查找机制完成的;
    • 2.子类可以重写父类上的方法「这样会导致父类其他的实例也受到影响」
    • 3.父类中私有或者公有属性方法,最后都会变为子类中公有的属性和方法;
  • 存在的问题:

    • 1.引用类型的属性被所有实例共享;
    • 2.创建实例时,不能给父类传值;

2.构造函数继承「经典继承」

Child方法中把Parent当做普通函数执行,让Parent中的this指向Child的实例,相当于给Child的实例设置了很多私有属性和方法

特点:

  • 1.只能继承父类私有的属性和方法「因为是把Parent当作普通函数执行,和其他原型上的属性和方法没有关系」
  • 2.父类私有的变为子类私有的

call继承

function Parent(){
    this.names=['Jack','Tom'];
}

Parent.prototype.getNames=function(){
    return this.names;
}

function Child(){
    //把Parent当普通函数执行,this是Child的实例
    Parent.call(this);
}

const c1=new Child();
c1.names.push('Lucy');
console.log(c1.names);//[ 'Jack', 'Tom', 'Lucy' ]

const c2=new Child();
console.log(c2.names);//[ 'Jack', 'Tom' ]

c1.getNames();//会报错
function Animal(name){
    this.name=name;
}

function Dog(name){
    //把Animal当普通函数执行,this是Dog的实例 「相当于给子类添加了name私有属性」
    Animal.call(this,name);// dog.name=name
}

const d=new Dog('dog');
console.log(d.name);//dog
  • 优点:

    • 1.避免了引用类型属性被所用实例共享问题
    • 2.可以在子类中向父类传值
  • 缺点:

    • 方法都在构造函数中定义,每次创建实例都会创建一遍方法

3.组合继承

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
    return this.name;
}

function Child(name, age) {
    Parent.call(this, name);
    this.age = age;
}

Child.prototype = new Parent();
//这个不要忘记
Child.prototype.constructor = Child;

const c1 = new Child('Jack', 18);
c1.colors.push('black');
console.log(c1.name);//Jack
console.log(c1.age);//18
console.log(c1.colors);//[ 'red', 'blue', 'green', 'black' ]

const c2 = new Child('Tom', 20);
console.log(c2.name);//Tom
console.log(c2.age);//20
console.log(c2.colors);//[ 'red', 'blue', 'green' ]
  • 优点:
    • 融合原型链继承和构造函数的优点,JS中常用的继承模式

3.寄生组合式继承

寄生组合式继承:call继承+类似于原型继承

特点:

父类私有,公有的属性和方法分别是子类实例的私有和公有属性和方法「推荐使用」

寄生组合式继承

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

Parent.prototype.getName=function(){
    console.log(this.name);
}

function Child(name,age){
    //Parent执行,继承私有属性
    Parent.call(this,name);
    this.age=age; 
}

// Object.create(Obj):创建一个空对象,让空对象的__proto__指向Obj
Child.prototype=Object.create(Parent.prototype);
Child.prototype.constructor=Child;

const c=new Child('Jack',18);
console.log(c.name);//Jack
console.log(c.age);//18
c.getName();//正常打印

实现Object.create(obj)方法:Object.create方法会创建一个空对象,空对象的__proto__指向obj

Object.create({name:'Jack'});

{
  __proto__:{
    name:'Jack'
  }
}
Object.create=function(obj){
  function Fn(){}
  Fn.prototype=obj;
  return new Fn();
}

4.ES6继承

  • 如果子类有constructor需要调用super(),相当于执行了A.call(this);
  • 如果没有constructor,默认底层已经调用了super()
class A {
    constructor(name){
        this.name=name;
    }

    getName(){
        return this.name
    }

    static sex='男';
}

class B extends A{
    constructor(name,age){
        super(name);
        this.age=age;
    }

    getAge(){
        return this.age;
    }
}

const b=new B('Jack',18);
console.log(b);//{ name: 'Jack', age: 18 }