Class 能夠經過 extends
關鍵字實現繼承。javascript
class Point {
}
class ColorPoint extends Point {
}
子類必須在constructor
方法中調用super
方法,不然新建實例時會報錯。java
class Point { /* ... */ } class ColorPoint extends Point { constructor() { } } let cp = new ColorPoint(); // ReferenceError
this
,而後再將父類的方法添加到this
上面(Parent.apply(this)
)。this
上面(因此必須先調用super
方法),而後再用子類的構造函數修改this
。若是子類沒有定義constructor
方法,這個方法會被默認添加app
class ColorPoint extends Point { } // 等同於 class ColorPoint extends Point { constructor(...args) { super(...args); } }
在子類的構造函數中,只有調用super
以後,纔可使用this
關鍵字,不然會報錯。函數
父類的靜態方法,也會被子類繼承。this
class A { static hello() { console.log('hello world'); } } class B extends A { } B.hello() // hello world
用來從子類上獲取父類。spa
Object.getPrototypeOf(ColorPoint) === Point // true
super
這個關鍵字,既能夠看成函數使用,也能夠看成對象使用。prototype
class A {}
class B extends A {
constructor() {
super();
}
}
super()
只能用在子類的構造函數之中,用在其餘地方就會報錯。code
class A {} class B extends A { m() { super(); // 報錯 } }
class A { p() { return 2; } } class B extends A { constructor() { super(); console.log(super.p()); // 2 } } let b = new B();
因爲super
指向父類的原型對象,因此定義在父類實例上的方法或屬性,是沒法經過super
調用的。對象
class A { constructor() { this.p = 2; } } class B extends A { get m() { return super.p; } } let b = new B(); b.m // undefined
若是屬性定義在父類的原型對象上,super
就能夠取到。blog
class A {} A.prototype.x = 2; class B extends A { constructor() { super(); console.log(super.x) // 2 } } let b = new B();
在子類普通方法中經過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
因爲this
指向子類實例,因此若是經過super
對某個屬性賦值,這時super
就是this
,賦值的屬性會變成子類實例的屬性。
若是super
做爲對象,用在靜態方法之中,這時super
將指向父類,而不是父類的原型對象。
在子類的靜態方法中經過super
調用父類的方法時,方法內部的this
指向當前的子類,而不是子類的實例。
注意,使用super
的時候,必須顯式指定是做爲函數、仍是做爲對象使用,不然會報錯。
因爲對象老是繼承其餘對象的,因此能夠在任意一個對象中,使用super
關鍵字。
__proto__
屬性,指向對應的構造函數的prototype
屬性。
Class 做爲構造函數的語法糖,同時有prototype
屬性和__proto__
屬性,所以同時存在兩條繼承鏈。
__proto__
屬性,表示構造函數的繼承,老是指向父類。prototype
屬性的__proto__
屬性,表示方法的繼承,老是指向父類的prototype
屬性。class A { } class B extends A { } B.__proto__ === A // true B.prototype.__proto__ === A.prototype // true
類的繼承是按照下面的模式實現的:
class A { } class B { } // B 的實例繼承 A 的實例 Object.setPrototypeOf(B.prototype, A.prototype); // B 繼承 A 的靜態屬性 Object.setPrototypeOf(B, A); const b = new B();
B
)的原型(__proto__
屬性)是父類(A
);B
)的原型對象(prototype
屬性)是父類的原型對象(prototype
屬性)的實例。extends
關鍵字後面能夠跟多種類型的值。只要是一個有prototype
屬性的函數,就能被繼承。
Object
類:class A extends Object { } A.__proto__ === Object // true A.prototype.__proto__ === Object.prototype // true
class A { } A.__proto__ === Function.prototype // true A.prototype.__proto__ === Object.prototype // true
子類實例的__proto__
屬性的__proto__
屬性,指向父類實例的__proto__
屬性。
//ColorPoint 繼承
var p1 = new Point(2, 3); var p2 = new ColorPoint(2, 3, 'red'); p2.__proto__ === p1.__proto__ // false p2.__proto__.__proto__ === p1.__proto__ // truePoint
所以,經過子類實例的__proto__.__proto__
屬性,能夠修改父類實例的行爲。
p2.__proto__.__proto__.printName = function () { console.log('Ha'); }; p1.printName() // "Ha"
ECMAScript 的原生構造函數大體有下面這些:
ES5中,生構造函數是沒法繼承的。
ES6中,容許繼承原生構造函數定義子類。
class MyArray extends Array { constructor(...args) { super(...args); } } var arr = new MyArray(); arr[0] = 12; arr.length // 1 arr.length = 0; arr[0] // undefined
extends
關鍵字不只能夠用來繼承類,還能夠用來繼承原生的構造函數。
注意,繼承Object
的子類,有一個行爲差別。沒法經過super
方法向父類Object
傳參
Mixin 指的是多個對象合成一個新的對象,新對象具備各個組成成員的接口。
const a = { a: 'a' }; const b = { b: 'b' }; const c = {...a, ...b}; // {a: 'a', b: 'b'}
function mix(...mixins) { class Mix {} for (let mixin of mixins) { copyProperties(Mix.prototype, mixin); // 拷貝實例屬性 copyProperties(Mix.prototype, Reflect.getPrototypeOf(mixin)); // 拷貝原型屬性 } return Mix; } function copyProperties(target, source) { for (let key of Reflect.ownKeys(source)) { if ( key !== "constructor" && key !== "prototype" && key !== "name" ) { let desc = Object.getOwnPropertyDescriptor(source, key); Object.defineProperty(target, key, desc); } } }
// 使用
class DistributedEdit extends mix(Loggable, Serializable) { // ... }