ES6知識點整理之----Class----繼承

Class 能夠經過 extends 關鍵字實現繼承。javascript

class Point {
}

class ColorPoint extends Point {
}

子類必須在constructor方法中調用super方法,不然新建實例時會報錯。java

class Point { /* ... */ }

class ColorPoint extends Point {
  constructor() {
  }
}

let cp = new ColorPoint(); // ReferenceError
  • ES5 的繼承,實質是先創造子類的實例對象this,而後再將父類的方法添加到this上面(Parent.apply(this))。
  • ES6 的繼承實質是先將父類實例對象的屬性和方法,加到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

Object.getPrototypeOf()

用來從子類上獲取父類。spa

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

super 關鍵字

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關鍵字。

類的 prototype 屬性和__proto__屬性

__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__屬性,指向父類實例的__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 的原生構造函數大體有下面這些:

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

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 模式的實現

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) { // ... }
相關文章
相關標籤/搜索