天天看點

ES6:Class與繼承

class

在ES5中,

function Person(name,age){
 this.name=name;
 this.age=age;
}
Person.prototype.showName=function(){
 console.log(this.name+","+this.age) ;
}
var s=new Person("op",20);
s.showName();
function Student(cose){
 this.cose=cose;
 this.showCode=function(){
  console.log("hello"+cose);
 }
}
var s1=new Student('for');
s1.showCode();
Student.prototype=new Person('some',18);//繼承
           

在ES6中,

let show='showAge';  
class Person{
 constructor(name,age){
  this.name=name;
  this.age=age;
 }
 showName(){
  console.log(this.name+this.age);
 }
 [show](){
  console.log("dddd");
 }
}
var p1=new Person("we",20);
p1.showName();
p1.showAge();//ddd
           

constructor 方法

class Point {
}

// 等同于
class Point {
  constructor() {}
}
           

類的所有執行個體共享一個原型對象

var p1 = new Point(2,3);
var p2 = new Point(3,2);

p1.__proto__ === p2.__proto__//true
           

上面代碼中,p1和p2都是Point的執行個體,它們的原型都是

Point.prototype

,是以

__proto__

屬性是相等的。

有get、set方法

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}
           

類的屬性名,可以采用表達式。

let methodName = 'getArea';

class Square {
  constructor(length) {
    // ...
  }

  [methodName]() {
    // ...
  }
}
           

類也可以使用表達式的形式定義

const MyClass = class Me {
  getClassName() {
    return Me.name;
  }
};
           

注意:

  1. 類和子產品的内部,預設就是嚴格模式,是以不需要使用

    use strict

    指定運作模式。
  2. 類不存在變量提升(hoist)
  3. 由于本質上,ES6 的類隻是 ES5 的構造函數的一層包裝,是以函數的許多特性都被Class繼承,包括

    name

    屬性。
  4. 如果某個方法之前加上星号(

    *

    ),就表示該方法是一個

    Generator

    函數。

this 的指向:

  • 類的方法内部如果含有

    this

    ,它預設指向類的執行個體。但是,必須非常小心,一旦單獨使用該方法,很可能報錯。

報錯解決方法:

  1. 在構造方法中綁定

    this

    ,這樣就不會找不到

    print

    方法了。
class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }
  //。。。
}
           
  1. 使用箭頭函數。
class Obj {
  constructor() {
    this.getThis = () => this;
  }
}
const myObj = new Obj();
myObj.getThis() === myObj // true
           
  1. 使用

    Proxy

    ,擷取方法的時候,自動綁定

    this

靜态方法

加上

static

關鍵字,就表示該方法不會被執行個體繼承,而是直接通過類來調用,這就稱為“靜态方法”。

  1. 注意,如果靜态方法包含

    this

    關鍵字,這個

    this

    指的是類,而不是執行個體。
class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

Foo.bar() // hello
           

上面代碼中,靜态方法

bar

調用了

this.baz

,這裡的

this

指的是

Foo

類,而不是

Foo

的執行個體,等同于調用

Foo.baz

。另外,從這個例子還可以看出,靜态方法可以與非靜态方法重名。

  1. 父類的靜态方法,可以被子類繼承。
  2. 靜态方法也是可以從

    super

    對象上調用的。
class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"
           

執行個體屬性除了定義在

constructor()

方法裡面的this上面,也可以定義在類的最頂層。

靜态屬性指的是 Class 本身的屬性,即

Class.propName

,而不是定義在執行個體對象(

this

)上的屬性。

new.target

屬性

new

是從構造函數生成執行個體對象的指令。ES6 為

new

指令引入了一個

new.target

屬性,該屬性一般用在構造函數之中,傳回new指令作用于的那個構造函數。如果構造函數不是通過new指令或

Reflect.construct()

調用的,

new.target

會傳回

undefined

,是以這個屬性可以用來确定構造函數是怎麼調用的。

function Person(name) {
  if (new.target !== undefined) {
    this.name = name;
  } else {
    throw new Error('必須使用 new 指令生成執行個體');
  }
}

// 另一種寫法
function Person(name) {
  if (new.target === Person) {
    this.name = name;
  } else {
    throw new Error('必須使用 new 指令生成執行個體');
  }
}

var person = new Person('張三'); // 正确
var notAPerson = Person.call(person, '張三');  // 報錯
           

Class 内部調用

new.target

,傳回目前 Class。

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    this.length = length;
    this.width = width;
  }
}

var obj = new Rectangle(3, 4); // 輸出 true
           

子類繼承父類時,

new.target

會傳回子類。

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    // ...
  }
}

class Square extends Rectangle {
  constructor(length, width) {
    super(length, width);
  }
}

var obj = new Square(3); // 輸出 false
           

利用這個特點,可以寫出不能獨立使用、必須繼承後才能使用的類。

class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('本類不能執行個體化');
    }
  }
}
class Rectangle extends Shape {
  constructor(length, width) {
    super();
    // ...
  }
}
var x = new Shape();  // 報錯
var y = new Rectangle(3, 4);  // 正确
           

繼承

Class 可以通過

extends

關鍵字實作繼承

class Point {
}

class ColorPoint extends Point {
}
           
class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 調用父類的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 調用父類的toString()
  }
}
           

Object.getPrototypeOf

方法可以用來從子類上擷取父類。可以使用這個方法判斷,一個類是否繼承了另一個類。

super

這個關鍵字

super

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

  1. 第一種情況,

    super

    作為函數調用時,代表父類的構造函數。ES6 要求,子類的構造函數必須執行一次super函數。
class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B
           

作為函數時,

super()

隻能用在子類的構造函數之中,用在其他地方就會報錯。

  1. 第二種情況,

    super

    作為對象時,在普通方法中,指向父類的原型對象;在靜态方法中,指向父類。
class A {
  p() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}

let b = new B();
           

類的

prototype

屬性和

__proto__

屬性

(1)子類的_

_proto__

屬性,表示構造函數的繼承,總是指向父類。

(2)子類prototype屬性的

__proto__

屬性,表示方法的繼承,總是指向父類的

prototype

屬性。

class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
           

第一種,子類繼承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
           
es6