zoukankan      html  css  js  c++  java
  • JS类继承常用方式发展史

    JS类继承常用方式发展史

    涉及知识点

    前言

    当JS被创造出来的时候,并没有使用当时最流行的类方式,像(JAVA C++)。具体原因为:

    对于有基于类的语言经验的开发人员来说,JavaScript 有点令人困惑 (如Java或C ++) ,因为它是动态的,并且本身不提供一个类实现。(在ES2015/ES6中引入了class关键字,但只是语法糖,JavaScript 仍然是基于原型的)。
    参考2

    1 构造函数方式继承

    1.1 通过构造函数多步骤完成继承单个对象

    /**
     * log-console.log的简单封装插件
     * 
     * @demo log(xxx) ==> console.log(xxx)
     * @explain 1 可以代替console.log()使用
     * @explain 2 可以运行在浏览器环境和NodeJs环境
     */
    function log() {
      console.log.apply(console, arguments);
    }
    
    /**
     * Student--学生抽象类-希望作为其中的一个父类
     * @param {String} guide 职业
     */
    function Student({guide}) {
      this.guide = guide;
    }
    Student.prototype.showGuide = function() {
      log(this.guide);
    }
    
    /**
     * People--子类,希望能够继承Student
     * 
     * @want People的实例对象拥有Student的特性,且可以自己扩展
     * 扩展后不影响Student
     */
    function People(props) {
      Student.call(this, props);
      this.country = props.country;
    }
    
    /**
     * 通过中间对象(函数)来实现People正确的原型链指向
     * 
     * @explain step1 MidFn 创建一个空函数
     * @explain step2 把MidFn函数的原型指向Student.prototype
     * @explain step3 把People原型对象指向MidFnde原型对象, MidFn的原型正好指向Student.prototype
     * @explain step4 把People原型的构造函数修复为People
     * @explain step5 People扩展方法测试是否会影响Student
     */
    // step1
    function MidFn() {}
    // step2
    MidFn.prototype = Student.prototype;
    // step3
    People.prototype = new MidFn();
    // step4
    People.prototype.construtor = People;
    // step5
    People.prototype.showCountry = function() {
      log(this.country);
    }
    
    const jeson = new People({
      guide: 'web前端',
      country: '中国'
    });
    
    jeson.showGuide();
    jeson.showCountry();
    log(jeson instanceof People); // => true
    log(jeson instanceof Student); // => true
    
    const student1 = new Student({
      guide: '全栈',
      country: 'Chinese'
    });
    student1.showGuide(); // => '全栈'
    student1.showCountry(); // => 出错
    
    

    分析 back

    • 1 通过创建一个中间对象函数,想办法将原型链修改为:
      new People() ----> People.prototype ----> Student.prototype ----> Object.prototype ----> null
      参考1
    • 2 为什么不只使用People.prototype = Student.prototype方式?
      • 答:这个方法简单粗暴也是可以使用的,为什么不使用呢?1 耦合性太高了,修改其中的一个类,另一个也会受影响,而且如果不知道Student.showGuide方法存在,子类写了这个方法,那么恭喜,Student.prototype.showGuide方法会被子类的People.prototype.showGuide替换掉,2 为了解耦(追求更好的方式--提高编程水平)
    • 3 子类的构造函数里面 父类.call(this, param1, param2)是为了实例化子类的时候将参数传给父类

    1.2 封装多步骤完成继承单个对象

    /**
     * inherits 封装继承的动作到inherits函数,简化代码,美化世界
     * @explain 通过封装4步骤完成继承
     */
    function inherits(Child, Parent) {
      const MidFn = function() {}
      MidFn.prototype = Parent.prototype;
      Child.prototype = new MidFn();
      Child.prototype.construtor = Child;
    }
    
    /**
     * 执行继承函数实现原型继承链
     */
    inherits(People, Student);
    

    分析 back

    • 1 封装继承的多步骤为一个inherits函数,简化代码参考1

    1.3 Object.create()方式完成继承单个对象

    /**
     * inherits 封装继承的动作到inherits函数,简化代码,美化世界
     * @explain 通过Object.create实现继承
     */
    function inherits(Child, Parent) {
      Child.prototype = Object.create(Parent.prototype);
      Child.prototype.constructor = Child;
    }
    
    /**
     * 执行继承函数实现原型继承链
     */
    inherits(People, Student);
    

    分析 back

    • 1 通过ES5提供的Object.create()完成继承
    • 2 由于new后生成的实例对象是对象(键值对),实例对象通过其构造函数的原型对象继承属性。而Object.create()可以完成对象的扩展参考3

    2 继承多个对象

    2.1 Object.create() Object.assign() 遍历复制实现多个对象继承

    /**
     * Student--学生类,作为People的一个父类
     * @class
     * @param {*} guide 职业
     */
    function Student(props) {
      this.guide = props.guide;
      this.class = props.class;
    }
    Student.prototype.showGuide = function() {
      log(this.guide);
    }
    
    /**
     * Sex--性别类,作为People的一个父类
     * @class 
     * @param {*} sex 性别
     */
    function Sex({sex}) {
      this.sex = sex;
    }
    Sex.prototype.showSex = function() {
      log(this.sex);
    }
    
    /**
     * People--人类--子类(想继承Student和Sex)
     * 
     * @want People的实例对象拥有Student Sex的特性,且可以自己扩展
     * 扩展后不影响Student和Sex
     */
    function People(props) {
      Student.call(this, props);
      Sex.call(this, props);
      this.country = props.country;
    }
    
    /**
     * 多个对象的继承
     * @param {Constrctor} Children 需要继承多个类的构造函数-这里为People
     * @param {Json} Parents 多个需要被继承的构造函数
     * @explain 1 实例化构造函数得到的实例对象是{...} 这种类型的对象和JSON结构一样
     * @explain 2 Object.assign(target, ...sources) 通过ES5提供的复制可枚举对象实现
     */
    function inheritMore(Children, ...Parents) {
      // console.log(Parents);
      objAssigns(Children, Parents);
      Children.prototype.constrctor = Children;
    }
    
    /**
     * objAssigns 继承多个对象
     * 
     * @param Children 需要继承多个类的构造函数-这里为People
     * @param {Array} Parents 多个需要被继承的构造函数构成的对象
     */
    function objAssigns(Children, Parents) {
      for (let i =0; i < Parents.length; i++) {
        Object.assign(Children.prototype, Parents[i].prototype);
      }
    }
    
    inheritMore(People, Student, Sex);
    
    
    const jeson = new People({
      guide: 'web前端',
      country: '中国',
      sex: '男',
      class: '3-6'
    });
    
    People.prototype.showCountry = function() {
      log(this.country);
    }
    
    jeson.showGuide(); // => 'web前端'
    jeson.showSex(); // => '男'
    jeson.showCountry(); // => '中国'
    log(jeson instanceof Student); // => false
    log(jeson instanceof Sex); // => false
    log(jeson instanceof People); // => true
    
    /**
     * 父类扩展看子类是否继承
     */
    Student.prototype.showClass = function() {
      log(this.class);
    }
    
    let tom = new People({
      class: '6-6'
    })
    Object.assign(People.prototype, Student.prototype); // 加上这句才能继承到父类扩展后的方法
    tom.showClass(); // => '6-6'
    
    

    分析 back

    • 1 通过遍历多次Object.assin()完成多个对象的继承 参考4
    • 2 是否可以通过多次创建空函数的方式实现多对象继承呢?实际是不可以的,子类的原型对象只会为最后一个类的实例对象,前面都被覆盖了,具体请看参考6
    • 3 (优化)这种方式在继承父对象后,如果父对象扩展了某个方法,子对象需要再次继承,是否可以自动继承父类扩展的方法,不用再次继承父类呢?知道的道友可以留言

    参考资料

    Back

  • 相关阅读:
    Android开发总结
    LeakCanary原理分析
    机器学习
    Kivy 中文教程 实例入门 简易画板 (Simple Paint App):2. 实现绘图功能
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 3. 循环
    Kivy 中文教程 实例入门 简易画板 (Simple Paint App):1. 自定义窗口部件 (widget)
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 2. 变量
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 1. 神秘朋友
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 0. 准备工作
    远程显示(操作) 服务器 GUI 程序(图形化界面) (基于 X11 Forwarding + Centos + MobaXterm)
  • 原文地址:https://www.cnblogs.com/easyweb/p/7657543.html
Copyright © 2011-2022 走看看