聊聊js里继承那点事儿~

152 阅读3分钟

「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战」。

前几天去面试,被问到了js继承方面的问题,由于自己很久没去关注过这块了,导致回答得不尽人意!= =,因此本篇文章就准备重新梳理下这块的知识,废话不多说,开搞!

ppx.jpg

es5里的继承

在es6的class出现之前,都是通过一些hack的方式实现继承的,具体来说,主要有如下三种

  • 原型链继承
  • 构造函数继承
  • 组合式继承

接下来就分别聊聊它们各自的实现

qidai.jpeg

原型链继承

顾名思义,主要是通过 原型链 的方式来实现继承,核心关键点就是 将父类的实例赋值给子类的原型对象,下面给出代码示例

    function Parent(name) {
        this.name = name
        this.list = [1,2,3]
    }
    Parent.prototype.getName = function(){
        return this.name
    }
    
    function Child(age) {
        this.age = age
    }
    //关键步骤
    Child.prototype = new Parent('name')
    Child.prototype.constructor = Child
    
    var instance = new Child(12)
    instance.getName() //输出 'name'

这种方式的优点如下:

  1. 子类可以共享父类原型对象上的方法,节省内存
  2. 实现方式很简单

缺点如下:

  1. 由于父类的实例属性被共享,会存在子类实例同时修改父类实例属性的情况,从而导致冲突

构造函数继承

顾名思义,这种方式是借用 构造函数 来继承的,而不是依靠原型对象,下面给出代码示例

     function Parent(name) {
        this.name = name
        this.list = [1,2,3]
        this.getName = function(){
            return this.name
        }
    }
    
    function Child(age) {
        //关键步骤
        Parent.call(this,'name')
        this.age = age
    }
    
    var instance = new Child(12)
    instance.getName() //输出 'name'

这种方式优点如下:

  1. 由于子类实例 各自 拥有父类实例属性的一个副本,因此可以避免原型链继承中的冲突问题
  2. 实现方式简单

缺点如下:

  1. 由于父类实例属性和方法都各自生成一个副本存在于子类实例中,因此无法高效地进行 方法 复用,导致浪费内存空间

组合式继承

这种方式结合了上述两种方式的优点

  1. 利用原型链继承实现 方法的复用
  2. 利用构造函数继承实现 属性的独立副本

代码示例如下

     function Parent(name) {
        this.name = name
        this.list = [1,2,3]
    }
    Parent.prototype.getName = function(){
        return this.name
    }
    
    function Child(age) {
       Parent.call(this,'name')
       this.age = age
   }
   Child.prototype = new Parent('name')
   Child.prototype.constructor = Child
   
   var instance = new Child(12)
   instance.getName() //输出 'name'

这种方式的唯一缺点就是 子类的原型对象上始终存在与其实例上一样的属性

es6里的继承

es6里的继承是官方推出的标准方式,主要利用 classextends 关键字来实现,代码示例如下

    class Parent {
        constructor(name) {
            this.name = name
        }
    }
    
    class Child extends Parent{
        constructor(name,age) {
            super(name)
            this.age = age
        }
    }
    
    const instance = new Child('name',12)
    instance.name // 输出 'name'

在es6里的继承,需要注意的一点是 子类是没有自己的this,都是通过super来继承父类的this,然后在自己的constructor里修改继承过来的this

因此在子类的constructor里,super必须在使用到this的地方 之前 去调用,否则就会报错,super的本质就是 父类的constructor函数

结语

前端领域的知识浩如烟海,只有不断的学习与巩固才能真正领悟,学习力很重要,希望我和正在阅读本文的你都会拥有,好啦,over!