【每日一题NO.52】ES6 类继承中 super 的作用

前端印记

共 2743字,需浏览 6分钟

 ·

2021-10-12 11:56

人生苦短,总需要一点仪式感。比如学前端~


  • 概念

  • super 当作函数

  • super 当做对象

    • 普通方法中指向父类的原型对象

    • 静态方法中指向父类本身

  • super中的this指向(总结)

概念

super 关键词用于访问和调用一个对象的父对象上的函数。
super 既可以当做函数使用,也可以当做对象使用

super([arguments]); // 调用父对象/父类 的构造函数

super.functionOnParent([arguments]); //调用 父对象/父类 上的方法

super 当作函数

super 作为函数调用时,代表父类的构造函数。

ES6 要求,子类的构造函数必须执行一次 super 函数

class Father {}

class Son extends Father {
  constructor() {
    super(); // 子类实现继承时,构造函数必须写super函数调用
  }
}

super代表父类的构造函数 被调用时,有以下几点需要注意:

  1. super 返回的对象是子类的实例。super 内部的 this 指的是 Son 的实例,这里调用 super()相当于Father.prototype.constructor.call(this)这么写。
  2. super 作为构造函数时,必须在使用 this 关键词之前使用,否则会报错
  3. super()调用,只能用在子类构造函数中,用在其他地方会报错

super 当做对象

super 作为对象时,有两种情况:

  1. 在普通方法中,指向父类的原型对象;
  2. 在静态方法中,指向父类本身

普通方法中指向父类的原型对象

  • super 指向的是父类原型对象,能获取父类原型对象上面的属性和方法,不能获取父类实例上的属性和方法
  • 通过 super 调用父类原型对象上的方法时,父类原型对象上方法内部的 this 指向子类实例
class Father {
  constructor() {
    this.name = "fatherName";
  }
  print() {
    console.log(this.name); // 代表父类构造函数使用的super(),函数内部的this指向子类实例
  }
}
class Son extends Father {
  constructor() {
    super();
    this.name = "sonName";
  }
  printSon() {
    super.print(); // print内部this指向Son的实例化对象
  }
}
let son = new Son();
son.printSon(); //'sonName'

/* 解析
Son.printSon函数中,super.print() 虽然相当于调用的是“Father.prototype.print()”,
但是Father.prototype.print()内部的 this 指向子类 Son 的实例,所以实际输出的是“sonName”而不是“fatherName”。
也就是说,printSon函数内实际上执行的是“super.print.call(this)”。
*/

  • 通过 super 来对某个属性赋值,这时 super 就是 子类this,赋值的属性会变为子类实例son的属性
// 阮一峰 es6文档的例子
class A {
  constructor() {
    this.x = 1;
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
    super.x = 3// 相当于 this.x = 3
    console.log(super.x); // undefined 父类没有增加x属性
    console.log(this.x); // 3 子类的x属性被改变
  }
}

let b = new B();
console.log(b.x); // 3

静态方法中指向父类本身

因为在静态方法中使用就跟原型没关系了,所以不代表原型也不代表实例对象

  • 在静态方法中,super 对象指的是父类,而不是父类的原型对象
  • 在静态方法中,super函数内部的this指向的是子类,而不是子类实例
// 阮一峰 es6文档的例子
class Parent {
  static myMethod(msg) { // 代号:1 静态方法, 会被3调用
    console.log("static", msg, this);
  }
  myMethod(msg) { // 代号:2 原型方法,会被4调用
    console.log("instance", msg, this);
  }
}
class Child extends Parent {
  static myMethod(msg) { // 代号3: 静态方法
    super.myMethod(msg); // super代表父类,这样写,相当于 Parent.static myMethod.call(Child)
  }
  myMethod(msg) { // 代号:4 原型方法
    super.myMethod(msg); // super代表父类的原型对象,这样写,相当于 Parent.prototype.myMethod.call(this)
  }
}
Child.myMethod('类对象-调用静态方法'); // 类,调用静态方法3
var child = new Child();
child.myMethod('实例对象-调用原型方法'); // 实例化对象,调用原型方法4


super中的this指向(总结)

  • 代表父类构造函数使用的super(),函数内部的this指向子类实例
  • 在普通方法中,代表父类原型对象使用的super,函数内部this指向子类实例
  • 在静态方法中,代表父类使用的super,函数内部this指向子类【就他特殊,单独记忆】

小小提示:使用super的时候,必须显式指定是作为函数还是作为对象使用,否则会报错。


所有《每日一题》的 知识大纲索引脑图 整理在此:https://www.yuque.com/dfe_evernote/interview/everyday
你也可以点击文末的 “阅读原文” 快速跳转


END
愿你历尽千帆,归来仍是少年。
浏览 25
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报