zoukankan      html  css  js  c++  java
  • 你真的了解[super ]关键字吗?

    image

    前言

    此篇文章是看了阮老师的es6教程,看到super关键字的时候觉得有必要总结梳理一下,原文还是参考 ECMAScript 6入门

    正文

    super 这个关键字,既可以当作函数使用,也可以当作对象使用。

    1.当作函数使用

    super作为函数调用时,代表父类的构造函数。ES6 要求 ,子类的构造函数必须执行一次super函数。

    即作为 函数 使用时下面的代码时 固定使用套路

    class A {
       constrctor(){
       }
    }
    
    class B extends A {
       constructor() {
         super();
       }
    }
    

    上面代码中的super()代表的谁??

    子类 B 的构造函数中的super(),代表 B调用父类 A 的构造函数执行。

    注意⚠️: 虽然这里的 super()代表了的 父类 A的构造函数,但是返回的却是 子类 B 的实例,即 super()内部的this 指的是 B, 因此 super() 在这里相当于 A.prototype.constructor.call(this)

    用一个大栗子来证明我的论点:

    class A {
        constrctor(){
            console.log(new.target.name)
        }
    }
    
    class B extends A{
        constructor(){
            super()
        }
    }
    new A() // A
    new B() // B
    

    在这段热气腾腾的代码中, new.target 指向当前正在执行的函数。

    啰嗦一下:

    new是从构造函数生成的实例对象的命令。ES6 为 new 命令引入了一个 new.target 属性,该属性一般用在构造函数之中,返回new命令作用于的那个构造函数。

    可以看到,在 super() 执行时,它指向的是子类 B 的构造函数,而不是父类 A 的构造函数。即 super() 内部的 this 指向的是 B

    还有一点需要注意 ⚠️ :

    作为函数时,super()只能用在子类的构造函数中,用在其他地方报错。

    // 错误写法
    class A {}
    
    class B extends A {
       m() {
         super(); // 报错
      }
    }
    

    总结一下:

    当super 作为函数的时候需要注意以下三点:

    1. 子类的构造函数必须执行一次super函数。
    // 再次重申,这是固定写法
    class A {
     constructor() {}
    }
    
    class B extends A {
      constructor() {
        super();// 这里表示 A.constructor()
      }
    }
    
    1. 子类的构造函数中的super()代表的是 子类调用父类的构造函数执行,这时候 super()中的this,即 父类.constructor 中的 this ,指向的是子类。

    3.super()只能用在子类的构造函数之中,用在其他地方就会报错。

    2.super作为对象时

    super 作为对象时,在普通方法中,指向父类的原型对象,在静态方法中指向父类。

    2.1 super作为对象在普通方法中应用

    上代码:

    class A {
        p(){
            return 2
        }
    }
    
    class B extends A {
        constrctor(){
            super();
            console.log(super.p());//2
        }
    }
    
    let b = new B();
    
    

    我们发现 在子类 Bsuper.p()执行结果为2。我们猜测这里的supsuper.p === A.prototype.p,验证一下发现结果是 true

    class A {
      p() {
        return 2;
      }
    }
    
    class B extends A {
      constructor() {
        super();
        console.log(super.p===A.prototype.p); // true
      }
    }
    
    let b = new B();
    

    也就是说这个时候 super 在普通的方法中,指向的是 A.prototype即子类中的super指向父类的原型对象。

    ⚠️注意点 1. :由于子类中的super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。

    class A {
       constructor() {
        this.s = 3;
       }
    }
    
    A.prototype.x = 2;
    
    class B extends A {
      constructor() {
        super();
        console.log(super.x) // 2  这里可以获取到父类原型上的x属性
      }
      
      get m(){
        return super.s
      }
    }
    
    let b = new B();
    console.log(b.m)// undefined  不能获取到定义在父类实例上的s属性
    

    通过上面的代码我们发现:

    子类 B 可以通过super获取到父类定义在原型上的属性,但是定义在父类 A的实例上的属性,无法获取到。

    注意点2⚠️:this指向

    ES6 规定,在子类普通方法中通过 super 调用父类的方法时,方法内部的 this 指向当前的子类实例。

    class A {
      constructor() {
        this.x = 1;
      }
      print() {
        console.log(this.x);
      }
    }
    
    class B extends A {
      constructor() {
        super();
        this.x = 2;
      }
      m() {
        super.print();// 这里等同于 A.proptotype.print()
      }
    }
    
    let b = new B();
    b.m() // 2
    

    上面代码中,super.print() 虽然调用的是A.prototype.print(),但是 A.prototype.print() 内部的this指向子类B的实例,导致输出的是2,而不是1。也就是说,实际上执行的是 super.print.call(this)

    注意点3⚠️:通过super进行赋值操作

    由于 this 指向子类实例,所以如果通过 super 对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。

    class A {
      constructor() {
        this.x = 1;
      }
    }
    
    class B extends A {
      constructor() {
        super();
        this.x = 2;
        super.x = 3;// 此时的 super 就是 b
        console.log(super.x); // undefined 等用于是 A.prototype.x
        console.log(this.x); // 3
      }
    }
    
    let b = new B();
    
    

    通过这两段代码我们发现了一个问题,当我通过 super 取值的时候取的是父类的原型上属性,但是当我通过 super 赋值的时候这时候 super 指向的是子类的实例。

    总结一下:

    通过上面的三个栗子得出,
    super作为对象在普通方法中应用时:

    1. 子类中的super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。
    2. 在子类普通方法中通过 super 调用父类的方法时,方法内部的 this 指向当前的子类实例。
    3. 通过 super 对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。

    2.2 super作为对象在静态方法中应用

    如果 super 作为对象,用在静态方法之中,这时 super 将指向父类,而不是父类的原型对象。

    class Parent {
      static myMethod(msg) {
        console.log('static', msg);
      }
    
      myMethod(msg) {
        console.log('instance', msg);
      }
    }
    
    class Child extends Parent {
      static myMethod(msg) {
      // 此时的 super 指的是父类,Parent
        super.myMethod(msg);
      }
    
      myMethod(msg) {
      // 普通函数 此时 super 是指 Parent.prototype
        super.myMethod(msg);
      }
    }
    
    Child.myMethod(1); // static 1
    
    var child = new Child();
    child.myMethod(2); // instance 2
    
    

    上面代码中,super在静态方法之中指向父类,在普通方法之中指向父类的原型对象。

    注意点1 ⚠️

    在子类的静态方法中通过 super 调用父类的方法时,方法内部的 this 指向当前的子类,而不是子类的实例。

    class A {
      constructor() {
        this.x = 1;
      }
      static print() {
        console.log(this.x);
      }
    }
    
    class B extends A {
      constructor() {
        super();
        this.x = 2;
      }
      static m() {
        super.print();// A.print 中的this指向当前的子类
      }
    }
    
    B.x = 3;
    B.m() // 3
    
    

    上面代码中,静态方法 B.m 里面,super.print指向父类的静态方法。这个方法里面的 this 指向的是 B,而不是 B 的实例。

    总结一下:

    在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。

    总之

    1. super作为函数,super()代表的就是父类构造函数,里面的this就是类的实例。
    2. super作为对象,在普通的方法中super.的语法指向的是父类的原型对象,在静态方法中使用super.语法的话就去父类的静态方法中找就行了。至于this指向问题,记住一点:如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,所以静态方法中的this只会指类而不是实例。
  • 相关阅读:
    MyBatis学习总结(11)——MyBatis动态Sql语句
    MyBatis学习总结(11)——MyBatis动态Sql语句
    Dubbo学习总结(3)——Dubbo-Admin管理平台和Zookeeper注册中心的搭建
    Dubbo学习总结(1)——Dubbo入门基础与实例讲解
    Dubbo学习总结(1)——Dubbo入门基础与实例讲解
    Maven学习详解(13)——Maven常用命令大全与pom文件讲解
    3分钟了解ServiceStage 应用智能化运维【华为云分享】
    OBS带你玩转图片
    高性能Web动画和渲染原理系列(3)——transform和opacity为什么高性能
    【Python成长之路】Python爬虫 --requests库爬取网站乱码(xe4xb8xb0xe5xa)的解决方法
  • 原文地址:https://www.cnblogs.com/chenshufang/p/10276662.html
Copyright © 2011-2022 走看看