深刻學習 es6 class

es6 引入的 class 類實質上是 JavaScript 基於原型繼承的語法糖。javascript

function Animal(name) {
  this.name = name;
}

Animal.prototype.sayHi = () => {
  return `Hello ${this.name}`;
};

// 等同於

class Animal {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    return `Hello ${this.name}`;
  }
}
複製代碼

類由兩部分組成:類聲明,類表達式java

  1. 類聲明
class Animal {
  constructor(name) {
    this.name = name;
  }
}

複製代碼

類其實是個特殊的函數,普通函數聲明和類函數聲明有一個重要的區別就是函數 聲明會提高,而類聲明不會。若是先訪問,後聲明就會拋出相似於下面的錯誤。es6

let animal = new Animal();
// Uncaught ReferenceError: Cannot access 'Animal' before initialization

class Animal {}
複製代碼
  1. 類表達式

類表達式能夠是被命名的或匿名的,(ps: 類表達式也一樣受到類聲明中提到的提高問題的困擾。)函數

// 匿名類
let Animal = class {
  constructor(name) {
    this.name = name;
  }
};

// 命名類
let Animal = class Cat {
  constructor(name) {
    this.name = name;
  }

  getClassName() {
    return Cat.name;
  }
};
複製代碼

此時類名字Cat只能在 class 內部使用,指代當前類,在類的外部只能用Animalui

  1. 構造函數 constructor方法是類的默認方法,經過new建立對象實例時,自動會調用該方法, 一個類必須擁有constructor方法,若是沒有寫,JavaScript 引擎會默認加上空的constructor方法。
class Animal {}

// 等同於

class Animal {
  constructor() {}
}
複製代碼

constructor方法默認返回實例對象(既this),徹底能夠指定返回另一個對象this

class Animal {
  constructor() {
    return Object.create(null);
  }
}

new Animal() instanceof Animal; // false;
複製代碼

上面代碼中,constructor函數返回一個全新的對象,結果致使實例對象不是Animal類的實例。spa

  1. 嚴格模式 類和模塊的內部,默認就是嚴格模式,好比,構造函數,靜態方法,原型方法,getter 和 setter 都在嚴格模式下執行。prototype

  2. 類的實例 類的實例,經過 new 建立, 建立時會自動調用構造函數code

class Animal {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    return "My name is " + this.name;
  }
}

let animal = new Animal("rudy");
animal.sayHi(); // My name is rudy
複製代碼
  1. 存取器 與 ES5 同樣,在類的內部可使用getset關鍵字,對某個屬性設置存取 函數和取值函數,攔截該屬性的存取行爲。
class Animal {
  constructor(name) {
    this.name = name;
  }

  get name() {
    return "rudy";
  }

  set name(value) {
    console.log("setter, " + this.value);
  }
}

let animal = new Animal("rudy");
animal.name = "Tom"; // setter, Tom
console.log(a.name); // rudy
複製代碼
  1. 靜態方法 使用static修飾符修飾的方法稱爲靜態,它們不須要實例化,直接經過類來調用。
class Animal {
  static sayHi(name) {
    console.log("i am " + name);
  }
}

let animal = new Animal();
Animal.sayHi("rudy"); // i am rudy
animal.sayHi("rudy"); // Uncaught TypeError: animal.sayHi is not a function
複製代碼
  1. 實例屬性,靜態屬性 ES6 中的實例屬性只能經過構造函數中的this.xxx來定義,但最近 ES7 中能夠直接在類裏面定義:
class Animal {
  name = "rudy";
  static value = 11;
  sayHi() {
    console.log(`hello, ${this.name}`);
  }
}

let animal = new Animal();
animal.sayHi(); // hello, rudy
Animal.value; // 11
animal.value; // undefiend
複製代碼
  1. 類的繼承 使用extends關鍵字實現繼承,子類中使用super關鍵字來調用父類的構造函數和方法。
class Animal {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    return "this is " + this.name;
  }
}

class Cat extends Animal {
  constructor(name, value) {
    super(name); // 調用父類的 constructor(name)
    this.value = value;
  }

  sayHi() {
    return `omg, ${super.sayHi()} it is ${this.value}`;
  }
}

let cat = new Cat("Tom", 11);
cat.sayHi(); // omg, this is Tom it is 11;
複製代碼
  1. super 關鍵字 super這個關鍵字,既能夠當着函數使用,也能夠當着對象使用。兩種狀況下,用法徹底不一樣。

第一種狀況,super做爲函數調用時,表明父類的構造函數。ES6 要求,字類的構造函數必須執行一次super函數。對象

class Animal {}

class Cat extends Animal {
  constructor() {
    super();
  }
}
複製代碼

上面代碼中,子類Cat的構造函數中的super(),表明調用父類的構造函數,這是必須的,否在 JavaScript 引擎會報錯。 注意,super雖然表明了父類Animal的構造函數,可是返回的是字類Cat的實例,既super內部的this指的是Cat的實例,所以super()在這裏至關於 Animal.prototype.constructor.call(this)

class Animal {
  constructor() {
    console.log(new.target.name); // new.target指向當前正在執行的函數
  }
}

class Cat extends Animal {
  constructor() {
    super();
  }
}

new Animal(); // Animal;
new Cat(); // Cat;
複製代碼

能夠看出,在super()執行時,它指向的是子類Cat的構造函數,而不是父類Animal的構造函數,也就是說super內部的this指向是Cat

做爲函數時,super()只能用在子類的構造函數之中,用在其餘地方就會報錯。

class Animal {}

class Cat extends Animal {
  hi() {
    super(); // Uncaught SyntaxError: 'super' keyword unexpected here
  }
}
複製代碼

第二種狀況,super做爲對象時:

  1. 在普通方法中,指向父類的原型對象,
  2. 在靜態方法中,指向父類。
class Animal {
  getName() {
    return "rudy";
  }
}

class Cat extends Animal {
  constructor() {
    super();
    console.log(super.getName());
  }
}

let cat = new Cat(); // rudy;
複製代碼

上面代碼中,子類Cat中的super.getName(),就是將super看成一個對象使用,這時,super在普通方法中,指向的是Animal.prototypesuper.getName()至關於Animal.prototype.getName()

這裏須要注意,因爲super指向的是父類原型對象,因此定義在父類實例上的方法和屬性,是沒法經過super獲取到的。

class Animal {
  constructor() {
    this.name = "rudy";
  }
}

class Cat extends Animal {
  constructor() {
    super();
  }
  getName() {
    return super.name;
  }
}

let cat = new Cat();
cat.getName(); // undefined;
複製代碼

上面代碼中,name是父類實例的屬性,而不是父類原型對象的屬性,因此super.name引用不到它。

用在靜態方法中,super將指向父類,而不是父類的原型對象。

class Animal {
  static getName(name) {
    console.log("static", name);
  }

  getName(name) {
    console.log("instance", name);
  }
}

class Cat extends Animal {
  constructor() {
    super();
  }

  static getName(name) {
    super.getName(name);
  }

  getName(name) {
    super.getName(name);
  }
}

Cat.getName("rudy"); // static rudy;

let cat = new Cat();
cat.getName("tom"); // instance tom;
複製代碼

在上面代碼中,super在靜態方法中指向父類,在普通方法中指向父類的原型對象。

另外,在字類的靜態方法中經過super調用父類的方法時,方法內部的this指向當前的子類,而不是子類實例。

class Animal {
  constructor() {
    this.name = "rudy";
  }

  static print() {
    console.log(this.name);
  }
}

class Cat extends Animal {
  constructor() {
    super();
    this.name = 2;
  }

  static print() {
    super.print();
  }
}

Cat.name = "Tom";
Cat.print(); // Tom;
複製代碼

參考資料

  1. ES6
  2. TS class類
相關文章
相關標籤/搜索