es6中用class和extends關鍵字來實現繼承。ES6中引入了class關鍵字來宣告類, 而class(類)可通過extends關鍵字實現繼承,讓子類繼承父類別的屬性和方法,語法「class 父類別名{...} class 子類名 extends 父類別名{...};」。
本教學操作環境:windows7系統、ECMAScript 6版、Dell G3電腦。
es6中可利用class關鍵字配合extends關鍵字來實現繼承。
1.簡介
Class可以通過extends關鍵字實現繼承,讓子類繼承父類別的屬性和方法。這比 ES5 的通過修改原型鏈實現繼承,要清晰和方便很多。
//父類別
class Point {
...
}
//子類
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color;
}
toString() {
return this.color + '' + super.toString(); // 呼叫父類別的toString方法
}
}
登入後複製
上面程式碼中,constructor方法和toString方法內部,都出現了super關鍵字,super在這裡表示父類別的建構函式,用來新建一個父類別的範例物件。
ES6規定,子類必須在constructor方法中呼叫super(),否則會報錯,這是因為子類自己的this物件,必須先通過父類別的建構函式完成塑造,得到與父類別同樣的範例屬性和方法,然後在新增子類自己的範例屬性和方法。
這是因為在ES5的繼承機制中,是先創造一個獨立的子類的範例物件,然後再將父類別的方法新增到這個物件上,即「範例在前,繼承在後」;ES6的繼承機制,則是先將父類別的屬性和方法,加到一個空的物件上面,然後再將該物件作為子類的範例,即「繼承在前,範例在後」。
這意味著,每次新建子類範例時,父類別的建構函式必定會先執行一次
class Foo {
constructor() {
console.log(1);
}
}
class Bar extends Foo {
constructor() {
super();
console.log(2);
}
}
const bar = new Bar(); // 1 2
登入後複製
上面的程式碼中,子類Bar新建範例時,會輸出1和2,這就是因子類建構函式呼叫super()時,會執行一次父類別建構函式。只有在子類別建構函式中呼叫super之後,才可以使用this關鍵字,否則會報錯。這是因為子類範例的構建,必須先完成父類別的繼承,只有super方法才能讓子類範例繼承父類別。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
this.color = color;
super(x, y);
this.color = color;
}
}"
登入後複製
如果子類沒有定義constructor方法,這個方法會預設新增,並且裡面會呼叫super,也就是說,不管有沒有顯示定義,任何一個子類都有constructor方法.
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
}
let cp = new ColorPoint(25, 8);
console.log(cp); //{x: 25, y: 8}
class ColorPoint extends Point {
constructor(...args) {
super(...args);
}
}
let cp = new ColorPoint(25, 8);
console.log(cp); //{x: 25, y: 8}
登入後複製
2.私有屬性和私有方法的繼承
父類別所有的屬性和方法,都會被子類繼承,除了私有的屬性和方法。子類無法繼承父類別的私有屬性,或者說私有屬性只能在定義它的class裡面使用。
class Foo {
#p = 1;
#m() {
console.log('hello');
}
}
class Bar extends Foo {
constructor() {
super();
console.log(this.#p); // 報錯
this.#m(); // 報錯
}
}
登入後複製
上面範例中,子類 Bar 呼叫父類別 Foo 的私有屬性或私有方法,都會報錯。
如果父類別定義了私有屬性的讀寫方法,子類就可以通過這些方法,讀寫私有屬性。
class Foo {
#p = 1;
getP() {
return this.#p;
}
}
class Bar extends Foo {
constructor() {
super();
console.log(this.getP()); // 1
}
}
登入後複製
3.靜態屬性和方法的繼承
父類別的靜態屬性和靜態方法,也會被子類繼承。
class A {
static hello() {
console.log('hello world');
}
}
class B extends A {
}
B.hello() // hello world
登入後複製
上面程式碼中,hello()
是A
類的靜態方法,B
繼承A
,也繼承了A
的靜態方法。
注意,靜態屬性是通過淺拷貝實現繼承的,如果繼承的屬性是原始資料型別,子類中操作繼承的靜態屬性不會影響到父類別,但如果繼承的屬性是一個物件,那麼子類修改這個屬性會印象到父類別
class C {
static foo = 100;
}
class D extends C {
constructor() {
super();
D.foo--;
}
}
const d = new D();
C.foo; // 100
D.foo; // 99
class A {
static foo = { n: 100 };
}
class B extends A {
constructor() {
super();
B.foo.n--;
}
}
const b = new B();
B.foo.n // 99
A.foo.n // 99
登入後複製
4.Object.getPrototypeOf()
Object.getPrototypeOf()
方法可以用來從子類上獲取父類別。
class Point { /*...*/ }
class ColorPoint extends Point { /*...*/ }
Object.getPrototypeOf(ColorPoint) === Point
// true
登入後複製
因此,可以使用這個方法判斷,一個類是否繼承了另一個類。
5.super關鍵字
super關鍵字既可以當做函數使用,也可以當做物件使用
第一種情況,super作為函數呼叫時,代表父類別的建構函式。呼叫super的作用是形成子類的this物件,把父類別的範例屬性和方法都放到這個this物件上面。
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A(); // A
new B(); // B
登入後複製
第二種情況,super作為物件時,在普通方法中,指向父類別的原型物件;在靜態方法中,指向父類別。
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呼叫的。如下所示:
class A {
constructor() {
this.p = 2;
}
}
class B extends A {
get m() {
return spuer.p;
}
}
let b = new B();
b.m // undefined
登入後複製
為了解決這種問題,可以將屬性定義在父類別的原型物件上
class A {};
A.prototype.x = 2;
class B extends A {
constructor() {
super();
console.log(super.x);
}
}
let b = new B();
登入後複製
ES6規定,在子類普通方法中通過super呼叫父類別的方法時,方法內部的this指向當前的子類範例
class A {
constructor() {
this.x = 1;
}
print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
m() {
super.print();
}
}
let b = new B();
b.m(); // 2
登入後複製
上面程式碼中,super.print()呼叫的是A.prototype.print(),但是此時方法內部的this指向是子類B的範例,所以輸出2。
由於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); //undefind
console.log(this.x); // 3
}
}
登入後複製
上面程式碼中,super.x
賦值為3
,這時等同於對this.x
賦值為3
。而當讀取super.x
的時候,讀的是A.prototype.x
,所以返回undefined
。
如果super作為物件,用在靜態方法之中,這時super將指向父類別,而不是父類別的原型物件。
class Parent {
static myMethod(msg) {
console.log('static', msg);
}
myMethod(msg) {
console.log('instance', msg);
}
}
class Children extends Parent {
static myMethod(msg) {
super.myMthod(msg);
}
myMethod(msg) {
super.myMethod(msg);
}
}
Child.myMethod(1); // static 1
var child = new Child();
child.myMethod(2); // instance 2
登入後複製
上面程式碼中,super
在靜態方法之中指向父類別,在普通方法之中指向父類別的原型物件。
另外,在子類的靜態方法中通過super呼叫父類別的方法時,方法內部的this指向當前的子類,而不是子類的範例
class A {
constructor() {
this.x = 1;
}
static print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
static m() {
super.print();
}
}
B.x = 3;
B.m() // 3
登入後複製
在靜態方法m中,super.print指向父類別的靜態方法,到那時this指向的是類B,而不是B的範例。
【推薦學習:】
以上就是es6中用什麼實現繼承的詳細內容,更多請關注TW511.COM其它相關文章!