你真的瞭解[super ]關鍵字嗎?

image

前言

此篇文章是看了阮老師的es6教程,看到super關鍵字的時候以爲有必要總結梳理一下,原文仍是參考 ECMAScript 6入門es6

正文

super 這個關鍵字,既能夠看成函數使用,也能夠看成對象使用。bash

1.看成函數使用

super做爲函數調用時,表明父類的構造函數。ES6 要求 ,子類的構造函數必須執行一次super函數。函數

即做爲 函數 使用時下面的代碼時 固定使用套路ui

class A {
   constrctor(){
   }
}

class B extends A {
   constructor() {
     super();
   }
}
複製代碼

上面代碼中的super()表明的誰??this

子類 B 的構造函數中的super(),表明 B調用父類 A 的構造函數執行。spa

注意⚠️: 雖然這裏的 super()表明了的 父類 A的構造函數,可是返回的倒是 子類 B 的實例,即 super()內部的this 指的是 B, 所以 super() 在這裏至關於 A.prototype.constructor.call(this)prototype

用一個大栗子來證實個人論點:code

class A {
    constrctor(){
        console.log(new.target.name)
    }
}

class B extends A{
    constructor(){
        super()
    }
}
new A() // A
new B() // B
複製代碼

在這段熱氣騰騰的代碼中, new.target 指向當前正在執行的函數。cdn

囉嗦一下:對象

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只會指類而不是實例。
相關文章
相關標籤/搜索