亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

ES6+ Class

1. 前言

上一節我們主要回顧了在 ES5 中使用構造函數的方式實現類了,并說明了如何實現類的繼承。從上一節的講解中,構造函數去實現類的繼承還是有諸多繁瑣的地方的,我們需要考慮子類和父類的關系,繼承中的細節都需要自己手動處理。本節我們將要學習 ES6 中的類的基本使用和類的繼承。在學習本節我們需要明確的是,ES6 中的類是基于現有語法的原型實現的,并沒有引入新的面相對象的模型。它的本質還是我們上節提到的構造函數,只是讓我們更加方便地使用,是基于原型的繼承的語法糖。

2. 基本用法

2.1 語法

上節我們在實現類的時候說,類不能被執行只能 new 來創建實例,當時我們是在內部手動處理的。在 ES6 中天然支持這個特性:

class Animal { }
Animal();
// Uncaught TypeError: Class constructor Animal cannot be invoked without 'new'

上面的代碼中我們定義了一個動物類,在控制臺中讓其執行,會看到如上的未捕獲的類型錯誤:意思是在沒有 new 的情況下是無法調用構造函數 Animal 的。使用 class 定義的類和使用構造函數定義類,在使用上是一樣的,只是創建類的方式不一樣。

上節我們知道如何創建實例上的屬性和原型上的屬性,那么使用 class 是怎么實現的呢?class 類提供了 constructor 方法,并在 new 的時候默認執行,所以在 constructor 函數內部在 this 上綁定實例上的屬性。而 原型上的方法則是在對象中直接添加屬性即可,實例如下:

class Animal {
  constructor() {
    this.type = '鳥類'
  }
  eat() {}
}
var a = new Animal();
console.log(a.hasOwnProperty('type'));	// true
console.log(a.hasOwnProperty('eat'));		// false

另外,在 ES7 中 class 還提供了一種方式在實例上綁定屬性,這種方式不需要 this,直接使用等號在 class 類中進行賦值。

class Animal {
  constructor() {
    this.type = '鳥類'
  }
  age='100'
  eat() {}
}
var a = new Animal();
console.log(a.hasOwnProperty('age'));	// true

需要注意的是,上面的等號賦值方式要在支持 ES7 的環境中才能執行。

2.2 get/set

當我們深入了解對象時我們就會知道屬性的 getter 和 setter ,提供了 get 和 set 兩個方法用于訪問和設置屬性。在 ES5 中有 Object.defineProperty() 方法可以對對象的屬性進行劫持,Vue2 的底層就是使用這個 API 實現的。當然 class 類其實也是一個對象,它也可以使用 get 的方式返回屬性值。如下實例:

class Animal {
  constructor() {
    this.type = "鳥類";
    this._age = 8;
  }
  get a() {
    return this._age;
  }

  set a(newValue) {
    this._age = newValue;
  }
}

var animal = new Animal();
console.log(animal.a); // 8
animal.a = 10;
console.log(animal.a); // 10

上面代碼中我們就使用了 get 和 set 去獲取屬性值和設置屬性值。那我們思考一個問題,set 和 get 是自有屬性還是原型上的屬性呢?其實 get 和 set 還是 class 類上的一個方法,所以是原型上的方法。

console.log(a.hasOwnProperty('a'));	// false

2.3 static

ES6 提供了用于定義靜態屬性和方法的關鍵字 static ,靜態方法調用時不需要實例化該類,所以就不能通過實例去調用,但可以使用類直接去調用。

靜態方法通常用于為一個應用程序創建工具函數,下面我們來看一個長方形類,定義一個獲取長方形面積的靜態方法。

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  static getArea(r) {
    return r.width * r.height;
  }
}

const r = new Rectangle(5, 10);

console.log(Rectangle.getArea(r));	// 50

3. 繼承

3.1 extends

在上節構造函數中的繼承我們知道,子類的構造函數中,需要我們去手動執行父構造函數并綁定this,還需要將子類的構造函數的原型鏈執行父類的原型。ES6 中的繼承非常簡單,在創建子類時只需要使用關鍵字 extends 即可創建一個子類。

// 父類:動物
class Animal {
  constructor(name) {
    this.name = name;
  }
  eat() {
    console.log(this.name + '會吃飯!');
  }
  static getAge() {
		console.log('獲取' + this.name + '的年齡10歲了');
		return 10;
  }
}

// 子類:具體的動物——狗
class Dog extends Animal {}

上面的代碼中子類 Owl 繼承了 Animal,那這個時候我們都繼承了什么呢?從上面的學習中父類中有,this 上的屬性,原型上的方法和靜態方法。

var dog = new Dog('狗');

console.log('name:', dog.name);			// name: 狗
console.log('age:', Dog.getAge());	// age: 10
dog.eat();	// 狗會吃飯!

從上面代碼打印的結果,我們知道,實例 dog 已經繼承了 Animal 上的屬性和方法。在父類中對 eat 方法的定義不明確,所以在子類中我們重寫 eat 方法。

class Dog extends Animal {
  eat() {
    console.log(this.name + '會吃飯!');
  }
}
var dog = new Dog('狗');
dog.eat();	// 狗喜歡吃骨頭!

3.2 super

super 是 class 中的關鍵字,可以理解是父類的別名,用于調用對象的父對象上的函數。一般 super 有兩種情況:super 當做函數調用;一種是 super 當做父類的對象使用。

第一種情況下,super 關鍵字作為函數調用,它的作用是為了綁定 this。所以子類的構造函數必須執行一次 super。默認情況下,類中不寫 constructor 時,constructor 會自動執行 super, 并綁定 this 指向當前子類。

class A {}

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

上節中我們在創建子類時就去執行了父類并綁定了this,上面代碼中的 super 和 A.call(this) 是相同的。

第二種情況下,super 當作父類的對象來使用的,什么情況下會使用呢?當我們在子類中想使用父類的方法時可以使用 super 直接調用父類的方法即可。

class A {
  getCount() {
    return 7;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.getCount()); // 7
  }
}

let b = new B();

4. 小結

本節主要學習了 ES6 中 class 類的使用和相關的知識點,需要明確的是 class 類就是一個語法糖,底層還是基于現有的原型對象的繼承來實現的。所以要想深入理解 ES6 的 class 就需要對 ES5 中的構造函數有深入的理解,另外,我們可以使用 babel 進行轉譯,得到的代碼就是使用構造函數來實現的。