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

  • 相关阅读:
    Heritrix 3.1.0 源码解析(二十五)
    Heritrix 3.1.0 源码解析(二十八)
    获取某年某月的第一天和最后一天的Sql Server函数
    C# ToString()用法汇总
    数据库隐式类型转换
    sql server 中 SET identity_insert on
    Linq To DataTable
    ASP.NET Session详解[转载]
    CSS overflow 属性
    HTML相对路径(Relative Path)和绝对路径(Absolute Path)
  • 原文地址:https://www.cnblogs.com/easyweb/p/7657543.html
Copyright © 2011-2022 走看看