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的实例。
【推荐学习:javascript高级教程】
以上是es6中用什麼實作繼承的詳細內容。更多資訊請關注PHP中文網其他相關文章!