Class的繼承


 

1.簡介

ES6中,子類必須在constructor方法中調用super方法,否則新建實例時會報錯。這是因為子類自己的this對象,必須先通過父類的構造函數完成塑造,然后再加上子類自己的實例屬性和方法

ES5 的繼承,實質是先創造子類的實例對象this,然后再將父類的方法添加到this上面(Parent.apply(this)),它與ES6 的繼承機制完全不同

另一個需要注意的地方是,在子類的構造函數中,只有調用super之后,才可以使用this關鍵字,否則會報錯

 1 class Point {
 2   constructor(x, y) {
 3     this.x = x;
 4     this.y = y;
 5   }
 6 }
 7 
 8 class ColorPoint extends Point {
 9   constructor(x, y, color) {
10     this.color = color; // ReferenceError
11     super(x, y);
12     this.color = color; // 正確
13   }
14 }

 

 

2.Object.getPrototypeOf()

Object.getPrototypeOf方法可以用來從子類上獲取父類

1 Object.getPrototypeOf(ColorPoint) === Point
2 // true

因此,可以使用這個方法判斷,一個類是否繼承了另一個類

 

 

3.super 關鍵字

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

第一種情況,super代表了父類A的構造函數,但是返回的是子類B的實例,即super內部的this指的是B的實例,因此super()在這里相當於A.prototype.constructor.call(this)

 1 class A {
 2   constructor() {
 3     console.log(new.target.name);
 4   }
 5 }
 6 class B extends A {
 7   constructor() {
 8     super();
 9   }
10 }
11 new A() // A
12 new B() // B

上面代碼中,new.target指向當前正在執行的函數。可以看到,在super()執行時,它指向的是子類B的構造函數,而不是父類A的構造函數

作為函數時,super()只能用在子類的構造函數之中,用在其他地方就會報錯

 

第二種情況,super作為對象時,在普通方法中,指向父類的原型對象;在靜態方法中,指向父類

 1 class A {
 2   p() {
 3     return 2;
 4   }
 5 }
 6 class B extends A {
 7   constructor() {
 8     super();
 9     console.log(super.p()); // 2
10   }
11 }
12 
13 let b = new B();

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

這里需要注意,由於super指向父類的原型對象,所以定義在父類實例上的方法或屬性,是無法通過super調用的

 1 class A {
 2   constructor() {
 3     this.p = 2;
 4   }
 5 }
 6 class B extends A {
 7   get m() {
 8     return super.p;
 9   }
10 }
11 
12 let b = new B();
13 b.m // undefined

 

如果屬性定義在父類的原型對象上,super就可以取到

 1 class A {}
 2 A.prototype.x = 2;
 3 
 4 class B extends A {
 5   constructor() {
 6     super();
 7     console.log(super.x) // 2
 8   }
 9 }
10 
11 let b = new B();

 

另外,在子類的靜態方法中通過super調用父類的方法時,方法內部的this指向當前的子類,而不是子類的實例

 

注意,使用super的時候,必須顯式指定是作為函數、還是作為對象使用,否則會報錯

 1 class A {}
 2 
 3 class B extends A {
 4   constructor() {
 5     super();
 6     console.log(super.valueOf() instanceof B); // true
 7   }
 8 }
 9 
10 let b = new B();

上面代碼中,super.valueOf()表明super是一個對象,因此就不會報錯。同時,由於super使得this指向B的實例,所以super.valueOf()返回的是一個B的實例

最后,由於對象總是繼承其他對象的,所以可以在任意一個對象中,使用super關鍵字

 

 

4.類的 prototype 屬性和__proto__屬性

(1)子類的__proto__屬性,表示構造函數的繼承,總是指向父類。

(2)子類prototype屬性的__proto__屬性,表示方法的繼承,總是指向父類的prototype屬性

1 class A {
2 }
3 
4 class B extends A {
5 }
6 
7 B.__proto__ === A // true
8 B.prototype.__proto__ === A.prototype // true

 

這樣的結果是因為,類的繼承是按照下面的模式實現的

 1 class A {
 2 }
 3 
 4 class B {
 5 }
 6 
 7 // B 的實例繼承 A 的實例
 8 Object.setPrototypeOf(B.prototype, A.prototype);
 9 
10 // B 繼承 A 的靜態屬性
11 Object.setPrototypeOf(B, A);
12 
13 const b = new B();

《對象的擴展》一章給出過Object.setPrototypeOf方法的實現

1 Object.setPrototypeOf = function (obj, proto) {
2   obj.__proto__ = proto;
3   return obj;
4 }

因此,就得到了上面的結果

1 Object.setPrototypeOf(B.prototype, A.prototype);
2 // 等同於
3 B.prototype.__proto__ = A.prototype;
4 
5 Object.setPrototypeOf(B, A);
6 // 等同於
7 B.__proto__ = A;

這兩條繼承鏈,可以這樣理解:作為一個對象,子類(B)的原型(__proto__屬性)是父類(A);作為一個構造函數,子類(B)的原型對象(prototype屬性)是父類的原型對象(prototype屬性)的實例

 

子類實例的__proto__屬性的__proto__屬性,指向父類實例的__proto__屬性。也就是說,子類的原型的原型,是父類的原型

1 var p1 = new Point(2, 3);
2 var p2 = new ColorPoint(2, 3, 'red');
3 
4 p2.__proto__ === p1.__proto__ // false
5 p2.__proto__.__proto__ === p1.__proto__ // true

 

 

5.原生構造函數的繼承

原生構造函數是指語言內置的構造函數,通常用來生成數據結構。ECMAScript 的原生構造函數大致有下面這些

  • Boolean()
  • Number()
  • String()
  • Array()
  • Date()
  • Function()
  • RegExp()
  • Error()
  • Object()

ES6 允許繼承原生構造函數定義子類,因為 ES6 是先新建父類的實例對象this,然后再用子類的構造函數修飾this,使得父類的所有行為都可以繼承。下面是一個繼承Array的例子

 

 

6.Mixin 模式的實現

Mixin 指的是多個對象合成一個新的對象,新對象具有各個組成成員的接口。它的最簡單實現如下

1 const a = {
2   a: 'a'
3 };
4 const b = {
5   b: 'b'
6 };
7 const c = {...a, ...b}; // {a: 'a', b: 'b'}

上面代碼中,c對象是a對象和b對象的合成,具有兩者的接口


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM