ES6 class類的概念

概述
JavaScript語言的傳統(tǒng)方法是通過構造函數,定義并生成新對象。下面是一個例子。

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

基本上,ES6的class可以看作只是一個語法糖,它的絕大部分功能,ES5都可以做到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。上面的代碼用ES6的“類”改寫,就是下面這樣。

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

上面代碼定義了一個“類”,可以看到里面有一個constructor方法,這就是構造方法,而this關鍵字則代表實例對象。也就是說,ES5的構造函數Point,對應ES6的Point類的構造方法。

Point類除了構造方法,還定義了一個toString方法。注意,定義“類”的方法的時候,前面不需要加上function這個關鍵字,直接把函數定義放進去了就可以了。另外,方法之間不需要逗號分隔,加了會報錯。

    (1)在 class 類上添加的屬性都是在原型 prototype 上添加的

(2)new 實例的時候其實就是調用構造函數這個方法

(3)類的本質其實就是一個函數

(4)類中的this 指向實例對象

(5)添加的私有屬性都在構造函數中添加

(6)每個構造方法都會默認返回實例對象this,如果人為改變 return 返回值,返回基本數據類型 字符串、數字、布爾等,不會改變return this 的值;如果返回應引用數據類型 對象 數組,那么return this 就會失效,返回你返回的結果

     (7) 類相當于實例的原型,所有在類中定義的方法,都會被實例繼承。如果在一個方法前,加上static關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,這就稱為“靜態(tài)方法”。
class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

上面代碼中,Foo類的classMethod方法前有static關鍵字,表明該方法是一個靜態(tài)方法,可以直接在Foo類上調用(Foo.classMethod()),而不是在Foo類的實例上調用。如果在實例上調用靜態(tài)方法,會拋出一個錯誤,表示不存在該方法。

注意,如果靜態(tài)方法包含this關鍵字,這個this指的是類,而不是實例

       (8)父類的靜態(tài)方法,可以被子類繼承。可以直接調用 。也是可以從super對象上調用的。
class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'


class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"
     (8)Class 可以通過 extends 關鍵字實現繼承,這比 ES5 的通過修改原型鏈實現繼承,要清晰和方便很多
class Animate {
    constructor() {
        // 默認返回實例對象 this
    }
}
class Dog extends Animate {
    constructor() {
        super()
    }
}
  子類必須在 constructor 方法中調用super方法,否則新建實例時會報錯,子類就得不到 this 對象。這是因為子類自己的 this 對象,必須先通過父類的構造函數完成塑造,得到與父類同樣的實例屬性和方法,然后再對其進行加工。如果不調用 super 方法,子類就得不到this對象。

ES5 的繼承,實質是先創(chuàng)造子類的實例對象 this,然后再將父類的方法添加到 this 上面(Parent.apply(this))。ES6 的繼承機制完全不同,實質是先將父類實例對象的屬性和方法,加到 this上面(所以必須先調用super方法),然后再用子類的構造函數修改 this。

class Animate {
    constructor(x, y) {
        // 默認返回實例對象 this
    }
}
class Dog extends Animate {
    constructor(x, y, z) {
        this.z = z    //  ReferenceError
        super(x, y)   //  this 只能在super() 方法調用之后再使用
        this.z = z 
    }
}

如果在子類中也寫入 num 方法,和父類中的方法重名,這樣就會覆蓋父類的 num 方法

class Animate {
    constructor() {
        // 默認返回實例對象 this
    }
    num() {
        console.log('我是父類的num方法')
    }
}

class Dog extends Animate {
    constructor() {
        super()
    }
    num() {
        console.log('我是子類的num方法')
    }
}
var dog = new Dog()
dog.num()     //  我是子類的num方法

如果不想覆蓋而是想引用父類的 num 方法,那么就在子類的 num 方法中通過 super 來調用父類的 num 方法,super.num()

      (9)super 關鍵字詳細解讀

super這個關鍵字,既可以當作函數使用,也可以當作對象使用。在這兩種情況下,它的用法完全不同。

1.第一種情況,super作為函數調用時,代表父類的構造函數。ES6 要求,子類的構造函數必須執(zhí)行一次super函數。

class A {}

class B extends A {
  constructor() {
    super();
  }
}
···
上面代碼中,子類B的構造函數之中的super(),代表調用父類的構造函數。這是必須的,否則 JavaScript 引擎會報錯。

注意,super雖然代表了父類A的構造函數,但是返回的是子類B的實例,即super內部的this指的是B的實例,因此super()在這里相當于A.prototype.constructor.call(this)。

作為函數時,super()只能用在子類的構造函數之中,用在其他地方就會報錯。(就是只能寫在繼承類的constructor里面)

2.第二種情況,super作為對象時,在普通方法中,指向父類的原型對象;在靜態(tài)方法中,指向父類。

```javascript
class A {
  p() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}

let b = new B();
···
上面代碼中,子類B當中的super.p(),就是將super當作一個對象使用。這時,super在普通方法之中,指向A.prototype,所以super.p()就相當于A.prototype.p()。

這里需要注意,由于super指向父類的原型對象,所以定義在父類實例上的方法或屬性,是無法通過super調用的。
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容