《JavaScript》ES6/ES7-class詳解(二)

class

class,ES6引入的一個關鍵字,其用法基本上等同於ES5中的原型的繼承,所以class關鍵字的引入並沒有爲JavaScript添加新的繼承模型,而是僅僅作爲一個語法糖被使用,因此JS中的class和其他面嚮對象語言中的class是兩碼事;

因此,在學習class之前,如果能先了解prototype和繼承的運行機制,這樣對class的學習將事倍功效;

先看一個簡單的對比

//ES5的prototype
function ES5(a,b){
    this.a = a;
    this.b = b;
}
ES5.prototype = {
    func:function(){
        return this.a + this.b
    }
}

//ES6的
class ES6{
    constructor(a,b){
        this.a = a;
        this.b = b;
    }
    //es6增強字面量寫法
    func(){
        return this.a + this.b
    }
}

如果你瞭解過構造函數,prototype等,那麼相信你很快就可以通過對比,基本瞭解class的使用,發現其效果與構造函數大同小異,比如在ES5的用法中對參數的初始化等同於在class中的constructor函數的使用,而在prototype上定義的方法等同於在class內直接定義函數

下面正式記錄class的用法:

創建

類的創建和函數的創建一樣,都是通過關鍵字創建,函數中使用的是關鍵字function,類則是通過關鍵字class,同樣,類的聲明方式也有兩種:類聲明類表達式

類聲明

//類聲明
class ES6{
	constructor(){}
}

其中,類聲明和函數聲明不同,函數聲明會提升,但類聲明不會提升,如果在聲明前使用類,那麼會拋出一個錯誤;

let p = new Person();//報錯

class Person{}

類表達式

類表達式分爲:具名類表達式匿名類表達式,兩者的區別在於類的內部有一個默認屬性name,然後其值不同;

//具名類表達式
const ES6 = class ES6_1{
	constructor(){}
}
console.log(ES6.name)	//ES6_1

//匿名
const ES6 = class{
	constructor(){}
}
console.log(ES6.name)	//ES6

驗證

另外,如果你對類ES6進行類型驗證

console.log(typeof ES6)	//function

發現,其實這個ES6的類型是function類型,也從側面反應了其實是ES5構造函數那一套東西,既然是function類型,那麼在js中,所有function類型都有一個prototype屬性,如果要給prototype擴展方法,如下例,是不是就是給類擴展方法?

class ES6{}

Object.assign(ES6.prototype,{
  func1(){},
  func2(){}
})

事實確實可以,另外通過調用類ES6上的方法也可以判斷ES6的類型,假設現在有這樣一個類

 class Animal {
    constructor() { }
    speak() {  
        console.log(1);
    }
}

如果Animal是對象,那麼調用Animal.speak(),應該是可以執行的,但經過測試,不可以,調用speak方法必須通過Animal.prototype.speak()

Animal.speak();	//報錯
Animal.prototype.speak();	//1

從這個例子也可以看出,class其實就是原型對象的用法,它只是將定義在prototype上的方法,直接寫在了class內

constructor方法

在class中的constructor是一個特殊的方法,這種方法用於創建和初始化一個由class創建的對象,一個類只能擁有一個constructor方法;

class ES6{
	constructor(a,b){
  	this.a = a;
    this.b = b;
  }
}
//等同於
function ES6(a,b){
    this.a = a;
    this.b = b;
}

extends和super

extend,是一個關鍵字,作用是爲當前的類設置一個父類,設置後,可以從父類那麼繼承到父類上的方法;

class Animal {
    constructor(name) { this.name = name; }
    speak() {  
        console.log(this.name);
    }
}
// 創建DOg父類
class Dog extends Animal {
    speak() {
        console.log(`這是${this.name}`);
    }
}

let dog = new Dog("旺財");

console.log(dog.speak())	//旺財

我學到這裏的時候,會有個疑問,子類Dog中並沒有設置name屬性的地方,爲什麼dog.speak()會輸出旺財,經過實驗發現,子類中沒有定義自己的constructor,因此會直接使用父類中的contructor,給dog添加了一個name屬性,具體等了解了super再一起分析

super,也是一個關鍵字,作用是調用父級對象上的函數,一共有兩種用法,如下:

super(arguments)	//必須寫在contructor中

super.function(arguments)

**
super(arguments),這個用法必須寫在子類的contructor中,作用是調用父類的constructor,比如

 class Animal {
    constructor(name) { this.name = name; }
    speak() {  
        console.log(this.name);
    }
}
// 創建DOg父類
class Dog extends Animal {
    constructor(name) { 
      super(name);	//會調用父類上的contructor方法,並將name作爲其參數
    }
    speak() {
        console.log(`這是${this.name}`);
    }
}

如果子類有contructor方法,那麼必須在方法內寫上super(),參數可以根據實際情況寫或不寫;另外如果寫了super(),則必須在後續內容的前面,否則會報錯,例如:

 class Animal {
    constructor(name) { this.name = name; }
    speak() {  
        console.log(this.name);
    }
}
// 創建DOg父類
class Dog extends Animal {
    constructor(name) { 
      this.name = name;	//報錯,supe()必須寫在最前面
      super(name);	
    }
    speak() {
        console.log(`這是${this.name}`);
    }
}

super.function(arguments),就是調用父類上的方法,這種寫法沒有具體限制,不需要一定寫在contructor裏面

class Animal {
    constructor(name) {
        this.name = name;
    }
    speak() {
      	console.log(this)
        console.log(this.name);
    }
}
// 創建DOg父類
class Dog extends Animal {
    constructor(name) {
      super(name); // 調用超類構造函數並傳入name參數
    }
    speak() {
        super.speak();	
    }
}

let dog = new Dog("旺財");
dog.speak()	//new的實例化之後會返回一個對象,該對象中的this指向Dog,

爲了更好的理解super,看一個經典示例

class Animal {
    constructor(age) {
        this.age = 10;
    }
}

class Dog extends Animal {
    constructor() {
      super(); // 調用超類構造函數並傳入name參數
      
      console.log(this.age);	//此時Dog並沒有age屬性,因此沿着原型鏈查到父類上面的屬性,返回10
      
      this.age = 2;	//設置後,此時的Dog上有個age屬性,並且設置值爲2
      console.log(this.age);	//這時候Dog上有age了,因此值是2
      
      //根據規範,沒有爲什麼,就是規範,在子類中使用super對某個值進行賦值操作的時候等同與this
      //也就是super.age = 18; 等同於 this.age = 18;
      super.age = 18;	
      
    	//而super.age的值,等同與Animal.prototype.age,很明顯Animal的原型上沒有age屬性
      //this.age自然就是上面super.age的值
      console.log(super.age,this.age);	//undefined,18
    }
}
let dog = new Dog();

static

static,是一個關鍵字,這個關鍵字使用在類內的方法前面,比如

class ES6{
	constructor(a,b){
  	this.a = a;
    this.b = b;
  }
  static sum(){
  	return this.a + this.b
  }
}

使用static定義的方法稱爲靜態方法,靜態方法代表如果該類被實例化,那麼靜態方法不能被實例對象繼承,但是如果是類之間的繼承則是可以繼承到的,比如實例化上例中的ES6方法

const _es6 = new ES6();
_es6.sum()	//報錯,找不到該方法 

如果是通過extends繼承,那麼static靜態方法將可以被子類繼承

class Animal {
    constructor(name) {
        this.name = name;
    }
    static out(){
        return 10
    }
    speak() {  
        console.log(this)
    }
}
// 創建DOg父類
class Dog extends Animal {
    constructor(name) {
      super(name); // 調用超類構造函數並傳入name參數
    }
}
console.log(Dog.out())	//10

通過例子可以確認,static靜態方法可以在類之間繼承,但是一旦類被實例化,那麼實例化對象將無法繼承static靜態方法;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章