最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 梳理一下JS中的类继承

    正文概述 掘金(单调先生)   2021-02-05   538

    本篇文章中,主要探究的是有两个方面,一个是ES6之前类的继承方式,一个是ES6类继承与非ES6的区别

    ES6之前类的继承方式概览:

    • 类继承
    • 构造函数式继承
    • 组合式继承
    • 原型式继承
    • 寄生式继承
    • 寄生组合式继承

    类继承

    本质:将子类原型修改为父类实例

    // 父类
    function Teacher(name) {
        this.name = name;
        this.students = ['a', 'b', 'c']
    }
    
    Teacher.prototype.teach = function() {
        console.info(this.name + ' teach students')
    }
    
    // 子类
    function MathTeacher(name) {
        this.name = name;
    }
    // 子类的原型,指向父类的实例
    MathTeacher.prototype = new Teacher();
    
    // 校验
    const teacher = new MathTeacher('john');
    const teacher2 = new MathTeacher('lily');
    console.info(teacher instanceof Teacher); // true
    
    // 可访问父类属性和方法
    teacher.teach() //john teach students
    console.info(teacher.students) // ['a', 'b', 'c']
    
    React JSX

    缺点:

    • 修改来自原型的引用类型属性时,会有副作用:影响所有实例
    • 无法传递参数:不能向父类传递构造函数参数
    teacher.students.push('d');
    // teacher.students ['a', 'b', 'c', 'd']
    // teacher1.students ['a', 'b', 'c', 'd']
    
    React JSX

    构造函数式继承

    本质:在子类构造函数里使用call/apply来实现继承。盗用继承这个名称可能更贴合

    // 父类
    function Teacher(name) {
        this.name = name;
        this.students = ['a', 'b', 'c']
    }
    
    Teacher.prototype.teach = function() {
        console.info(this.name + ' teach students')
    }
    
    // 子类
    function MathTeacher(name) {
        Teacher.call(this, name);
    }
    
    // 校验
    const teacher = new MathTeacher('john');
    console.info(teacher instanceof Teacher); // false
    
    teacher.teach(); // throw error -> Uncaught TypeError: teacher.teach is not a function
    
    React JSX

    缺点:

    • 无法通过instanceOf校验
    • 无法调用父类方法及属性:即没有继承到

    组合式继承

    本质:组合以上两种继承方式(类式继承&构造函数式继承)

    // 父类
    function Teacher(name) {
        this.name = name;
        this.students = ['a', 'b', 'c']
    }
    
    Teacher.prototype.teach = function() {
        console.info(this.name + ' teach students')
    }
    
    // 子类
    function MathTeacher(name) {
        Teacher.call(this, name);
    }
    
    // !!diff
    MathTeacher.prototype = new Teacher();
    
    // 校验
    const teacher = new MathTeacher('john');
    const teacher1 = new MathTeacher('john');
    console.info(teacher instanceof Teacher); // true
    
    teacher.teach(); // john teach students
    teacher.students.push('d');
    
    React JSX

    组合继承基本解决了最开始两种继承方式的缺点,但是又产生了新的问题:父类构造函数被调用了两次

    原型式继承

    本质上是对类继承的一种封装,可等价于Object.create,核心就是借助原型基于已有的对象创建一个新的对象,同时还不必因此创建自定义类型

    function createObj(o) {
        function OO() {}
        OO.prototype = o;
        return new OO()
    }
    
    React JSX

    缺陷:同 类继承

    寄生式继承

    在原型式继承的基础上进行拓展,本质是依托一个内部对象来生成一个新对象,因此称为寄生

    // 父类
    function Teacher(name) {
        this.name = name;
        this.students = ['a', 'b', 'c']
    }
    
    Teacher.prototype.teach = function() {
        console.info(this.name + ' teach students')
    }
    
    const teacher = new Teacher('john');
    
    // 子类
    function MathTeacher(obj) {
        // 继承obj的原型
        const o = Object.create(obj);
        return o;
    }
    
    // 实例化子类,需传入父类
    const mathTeacher = new MathTeacher(teacher);
    
    React JSX

    缺陷:修改原型上的引用类型,会同步所有的子类型实例

    寄生组合式继承

    结合寄生继承与组合式继承,基本解决以上方式的问题

    // 核心继承方法
    function inherit(child, parent) {
        // 继承父类型的原型
    
        const o = Object.create(parent.prototype);
        // 重写子类原型
        child.prototype = o;
        // 重写子类被污染的constructor
        o.constructor = child;
    }
    
    // 父类
    function Teacher(name) {
        this.name = name;
        this.students = ['a', 'b', 'c']
    }
    
    Teacher.prototype.teach = function() {
        console.info(this.name + ' teach students')
    }
    
    const teacher = new Teacher('john');
    
    // 子类
    function MathTeacher(name) {
        // 继承父类属性
        Teacher.call(this, name);
    }
    
    // 继承
    inherit(MathTeacher, Teacher);
    
    MathTeacher.prototype.calc = function() {
        console.info(this.name + ' teach students to calc')
    }
    
    const mathTeacher = new MathTeacher('john');
    const mathTeacher2 = new MathTeacher('lily');
    
    mathTeacher.teach() // john teach students
    mathTeacher.students.push('d')
    console.info(mathTeacher.students, mathTeacher2.students)// 不互相影响
    
    React JSX

    最终达成的效果:

    • 子类继承父类的属性和方法,同时,属性也没有被创建在原型链上,因此,多个子类不会共享同一个属性
    • 子类可以传递动态参数给父类
    • 父类的构造函数只执行了一次

    缺陷:子类的方法必须要在继承后编写,否则继承时会被覆盖掉。什么意思呢?接上面的代码举个例子:

    // 父类
    function Parent() {/* ... */}
    // 子类
    function Child() {/* ... */}
    // 定义子类方法
    Child.prototype.fn = function() {/* ... */}
    // 继承
    inherit(Child, Parent);
    // 实例化子类
    const child = new Child();
    child.fn(); // throw error -> Uncaught TypeError: child.fn is not a function
    
    React JSX

    可以看到在上面的例子中,定义子类的方法发生在继承前,继承时方法被覆盖了,导致抛出异常。

    改进:针对该缺陷,可以改进继承方法

    // 核心继承方法
    function inherit(child, parent) {
        // 继承父类型的原型
        const o = Object.create(parent.prototype);
        // 重写子类原型 !!diff
        child.prototype = Object.assign(o, child.prototype)
        // 重写子类被污染的constructor
        o.constructor = child;
    }
    
    React JSX

    ES6 Class 继承

    ES6提供了很方便的定义类的语法糖class,使开发者可以很快速直观的创建一个类,而不用再去写一堆具有混淆性的function了。ES6的类也提供了很方便的继承方法,那就是关键字extends,使用如下

    class Teacher {
        constructor(name) {
            this.name = name;
        }
    
        teach() {
            console.info(this.name + ' teaches students')
        }
    }
    
    class MathTeacher extends Teacher {
        calc() {
            console.info(this.name + ' teaches students to calc')
        }
    }
    
    React JSX

    既然是语法糖,就有不用class实现类的形式,那么究竟是如何实现呢?我们可以用babel来翻译一下以上例子:

    "use strict";
    // 继承方法
    function _inheritsLoose(subClass, superClass) { 
    	subClass.prototype = Object.create(superClass.prototype); 
    	subClass.prototype.constructor = subClass; 
    	_setPrototypeOf(subClass, superClass); 
    }
    
    function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
    
    // 父类
    var Teacher = /*#__PURE__*/function () {
      function Teacher(name) {
        this.name = name;
      }
    
      var _proto = Teacher.prototype;
    
      _proto.teach = function teach() {
        console.info(this.name + ' teaches students');
      };
    
      return Teacher;
    }();
    
    // 子类
    var MathTeacher = /*#__PURE__*/function (_Teacher) {
      _inheritsLoose(MathTeacher, _Teacher);
    
      function MathTeacher() {
        return _Teacher.apply(this, arguments) || this;
      }
    
      var _proto2 = MathTeacher.prototype;
    
      _proto2.calc = function calc() {
        console.info(this.name + ' teaches students to calc');
      };
    
      return MathTeacher;
    }(Teacher);
    
    React JSX

    看起来是不是很熟悉,没错,就是寄生组合式继承

    文章基于此文章进行分析


    起源地下载网 » 梳理一下JS中的类继承

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元