JS 实现继承的几种方式

继承的目的简单讲就是要使用父类的实例属性或原型属性

// 父类
function Animal(name, color) {
    // 实例属性
    this.name = name;
    this.color = color;
}
// 原型属性
Animal.prototype.getName = function() {
    return this.name;
}
// 子类
function Cat() {
    xxxxxx
}

构造函数继承

原理:在子类的构造函数中调用父类的构造函数

function Cat(name, color) {
    // 调用了父类的构造函数,将其中的this指向了子类的实例
    Animal.call(this, name, color);
    this.xxx = xxx;
}

优点:可以向父类构造函数传参
缺点:只能继承父类的实例属性

原型链继承 之 间接原型继承

原理:将子类原型指向父类的实例

Cat.prototype = new Animal()
Cat.prototype.constructor = Cat;

优点:

  1. 子类原型的修改不会影响到父类原型
  2. 既能继承实例属性,也能继承原型属性

缺点:

  1. 不方便给父类的构造函数传参,传也只是一次性的,只能在刚开始修改子类原型的时候传
  2. 子类的原型是父类的实例,继承自父类实例上的属性对子类实例来说是公有的

注意:给子类添加原型方法一定要在 Cat.prototype = new Animal() 之后

原型链继承 之 直接原型继承

原理:将子类的原型指向父类的原型

// 这种做法比较激进,弊端很大
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;

优点:貌似没有什么优点,不推荐使用
缺点:

  1. 子类没有自己的原型,就没有自己的公共方法
  2. 修改子类的原型会影响到父类原型,即以后不能再给子类添加原型属性
  3. 只能继承父类的原型属性

原型继承 之 使用空对象作为媒介

原理:emmmm,看代码

function extend(Parent) {
    function F(){}
    F.prototype = Parent.prototype;
    return new F();
}
Cat.prototype = extend(Animal);
// 或者 Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;

优点:

  1. 和直接原型继承相比,修改子类的原型不会影响到父类
  2. 和间接原型继承相比,子类的原型是一个空对象,内存占用较少(当然后期也可以加原型属性)

缺点:只能继承原型属性

拷贝继承

原理:通过 for-in 将父类上的原型属性复制到子类原型

function extend(Child, Parent) {
    var c = Child.prototype;
    var p = Child.prototype;
    for(var key in p) {
        if(!p.hasProperty(key)) return;
        c[key] = p[key];
    }
}

优点:emmmm...子类在继承前或继承后都可以设置原型属性
缺点:只能继承父类的原型属性,性能较差?

实际上也可以复制父类的实例,从而继承父类的实例属性,不过缺点和间接原型继承一样,父类实例的实例属性被子类实例共享。

组合继承

原理:构造函数继承 + 间接原型继承

优点:既能继承实例属性,又能继承原型属性

缺点:

  1. 间接原型继承的缺点:父类实例的实例属性被子类实例共享
  2. 调用了两次父类的构造函数,生成了两个父类实例,开销大 ----- 这里我不这么认为,我觉得使用call来调用父类构造函数并不是new调用,而是把父类的构造函数当成普通函数执行,仅仅是把this指向了子类的实例,执行父类构造函数仅仅是给子类实例添加私有属性,并不会创建父类实例。

寄生组合式继承(推荐)

原理:构造函数继承 + 空对象媒介

优点:和组合继承相比,这样就不会生成父类实例了,且子类原型是个空对象,开销较小。

总结

只继承实例属性

  1. 构造函数继承(能给父类构造函数灵活传参)

只继承原型属性

  1. 直接原型继承(不推荐)
  2. 空对象媒介(开销小)
  3. 拷贝继承(也可拷贝父类实例)

既继承实例属性,又继承原型属性

  1. 间接原型继承
  2. 组合继承(构造函数继承 + 间接原型继承)
  3. 寄生组合继承(构造函数继承 + 空对象媒介)
posted @ 2020-03-21 13:12  狐耳  阅读(243)  评论(0编辑  收藏  举报