大多數面向對象的編程語言都支持類和類繼承的特性,而JS卻不支持這些特性,只能經過其餘方法定義並關聯多個類似的對象,這種狀態一直延續到了ES5。因爲相似的庫層出不窮,最終仍是在ECMAScript 6中引入了類的特性。本文將詳細介紹ES6中的類,ES6 的 class
屬於一種「語法糖」,因此只是寫法更加優雅,更加像面對對象的編程,其思想和 ES5 是一致的。編程
function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function() { return '(' + this.x + ',' + this.y + ')'; }
等同於編程語言
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ',' + this.y + ')'; } }
其中 constructor 方法是類的構造函數,是一個默認方法,經過 new 命令建立對象實例時,自動調用該方法。一個類必須有 constructor 方法,若是沒有顯式定義,一個默認的 consructor 方法會被默認添加。因此即便你沒有添加構造函數,也是會有一個默認的構造函數的。通常 constructor 方法返回實例對象 this ,可是也能夠指定 constructor 方法返回一個全新的對象,讓返回的實例對象不是該類的實例。函數
下面好好分析一下 super
關鍵字的做用:this
super
這個關鍵字,既能夠當作函數使用,也能夠當作對象使用。這兩種狀況下,它的用法徹底不用。spa
1. 當作函數使用prototype
class A {} class B extends A { constructor() { super(); // ES6 要求,子類的構造函數必須執行一次 super 函數,不然會報錯。 } }
注:在 constructor 中必須調用 super 方法,由於子類沒有本身的 this 對象,而是繼承父類的 this 對象,而後對其進行加工,而 super 就表明了父類的構造函數。super 雖然表明了父類 A 的構造函數,可是返回的是子類 B 的實例,即 super 內部的 this 指的是 B,所以 super() 在這裏至關於 ```A.prototype.constructor.call(this, props)``。code
class A { constructor() { console.log(new.target.name); // new.target 指向當前正在執行的函數 } } class B extends A { constructor() { super(); } } new A(); // A new B(); // B
能夠看到,在 super()
執行時,它指向的是 子類 B 的構造函數,而不是父類 A 的構造函數。也就是說,super()
內部的 this
指向的是 B。對象
2. 當作對象使用blog
在普通方法中,指向父類的原型對象;在靜態方法中,指向父類。繼承
class A { c() { return 2; } } class B extends A { constructor() { super(); console.log(super.c()); // 2 } } let b = new B();
上面代碼中,子類 B 當中的 super.c()
,就是將 super
看成一個對象使用。這時,super
在普通方法之中,指向 A.prototype
,因此 super.c()
就至關於 A.prototype.c()
。
經過 super
調用父類的方法時,super
會綁定子類的 this
。
class A { constructor() { this.x = 1; } s() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 2; } m() { super.s(); } } let b = new B(); b.m(); // 2
上面代碼中,super.s() 雖然調用的是 A.prototytpe.s(),可是 A.prototytpe.s()會綁定子類 B 的 this,致使輸出的是 2,而不是 1。也就是說,實際上執行的是 super.s.call(this)。
因爲綁定子類的 this,因此若是經過 super 對某個屬性賦值,這時 super 就是 this,賦值的屬性會變成子類實例的屬性。
class A { constructor() { this.x = 1; } } class B extends A { constructor() { super(); this.x = 2; super.x = 3; console.log(super.x); // undefined console.log(this.x); // 3 } } let b = new B();
上面代碼中,super.x
賦值爲 3,這時等同於對 this.x
賦值爲 3。而當讀取 super.x
的時候,調用的是 A.prototype.x
,但並無 x
方法,因此返回 undefined。
注意,使用 super
的時候,必須顯式指定是做爲函數,仍是做爲對象使用,不然會報錯。
class A {} class B extends A { constructor() { super(); console.log(super); // 報錯 } }
上面代碼中,console.log(super); 的當中的 super,沒法看出是做爲函數使用,仍是做爲對象使用,因此 JavaScript 引擎解析代碼的時候就會報錯。這是,若是能清晰的代表 super 的數據類型,就不會報錯。
最後,因爲對象老是繼承其餘對象的,因此能夠在任意一個對象中,使用 super 關鍵字。
結語:ES6 的 class 畢竟是一個「語法糖」,因此只要理解了 JavaScript 中對象的概念和麪向對象的思想,class 就不難理解啦。