1. 程式人生 > >ES6詳解三:class

ES6詳解三:class

class是es6引入的最重要特性之一。在沒有class之前,我們只能通過原型鏈來模擬類。

基本用法

如果你用過java這樣的純面嚮物件語言,那麼你會對class的語法非常熟悉。

class People {
    constructor(name) { //建構函式
          this.name = name;
    }
    sayName() {
          console.log(this.name);
    }
}

上面定義了一個People類,他有一個屬性 name 和一個方法 sayName(),還有一個建構函式;
你可以這樣使用這個類:

var p = new People("Tom");
p.sayName();

就像函式有函式宣告和函式表示式兩種定義方式,類也可以通過類表示式來定義:

let People = class {
    constructor(name) { //建構函式
          this.name = name;
    }
    sayName() {
          console.log(this.name);
    }
  }

你可能以為類宣告和類表示式的區別在於變數提升的不同。但是事實是無論是類宣告還是類表示式的方式來定義,都不會有變數提升。所以下面的寫法是錯的:

var p = new People("Tom");    //錯誤,People 未定義
class People {
    //...
};

類中的所有方法預設都是 strict mode,所以不用再次聲明瞭。

繼承

通過關鍵字 extends 來繼承一個類,並且,可以通過 super 關鍵字來引用父類。

class People {
    constructor(name) { //建構函式
          this.name = name;
    }
    sayName() {
          console.log(this.name);
    }
}

class Student extends People {
    constructor(name, grade) { //建構函式
        super(name);    //呼叫父類建構函式
          this.grade = grade;
    }
    sayGrade() {
          console.log(this.grade);
    }
}

上面的例子中我們定義了一個 Student ,他是 People 的子類。

注意我們在 constructor 中是如何通過 super 呼叫父類的建構函式的。

getters & setters

現在我們可以通過 getset 關鍵字來定義 getters 和 setters 了。

下面我們給 name 屬性定義 getter 和 setter

class People {
    constructor(name) { //建構函式
          this.name = name;
    }
    get name() {
        return this._name.toUpperCase();
    }
    set name(name) {
        this._name = name;
    }
    sayName() {
          console.log(this.name);
    }
}
var p = new People("tom");
console.log(p.name);    //1
console.log(p._name);    //2
p.sayName();    //3

仔細看上面的例子,搞清楚最後三行分別會輸出什麼,就明白getter 和 setter該怎麼用了。

主要是要區分 this._name 和 this.name 的區別。因為我們定義了 name 的讀寫器,而沒有定義 _name 的讀寫器,所以訪問這兩個屬性的結果是不同的。

但是要注意一點,不要這樣寫:

set name(name) {
    this.name = name;
}

因為給 this.name 賦值的時候會呼叫 set name ,這樣會導致無限遞迴直到棧溢位。

靜態方法

通過 static 關鍵字定義靜態方法:

class People {
    constructor(name) { //建構函式
          this.name = name;
    }
    sayName() {
          console.log(this.name);
    }
    static formatName(name) {
        return name[0].toUpperCase() + name.sustr(1).toLowerCase();
    }
}

console.log(People.formatName("tom"));

靜態方法一般用來提供一些工具方法。

私有屬性

很不幸的時ES6並沒有提供對私有屬性的語法支援,但是我們可以通過閉包來實現私有屬性。

關於 WeakMap,會在下一篇部落格仔細講解。
為什麼要用WeakMap呢?因為WeakMap 用object作為key,並且是一個弱引用,也就是說,WeakMap對這個物件的引用並不會導致GA無法回收這個物件(GA計算物件引用數量的時候並不會計算弱引用)。

var People = (function() {
  var p = new WeakMap();
  class People {
    constructor(name) { //建構函式
          var privateProperties = {
        name: name
    };
    p.set(this, privateProperties);
    }
    sayName() {
          console.log(this.name);
    }

    get name() {
      return p.get(this).name;
    }
}
return People;
})();

var p = new People("tom");
console.log(p.name);
p.sayName();

var p2 = new People("bob");
console.log(p2.name);
p2.sayName();

除了用WeakMap,還可以用閉包+Symbol來實現私有屬性,參見後面專門講Symbol的文章。

參考