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方法可以用來從子類上獲取父類。可以使用這個方法判斷,一個類是否繼承了另一個類。

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

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章