公開與私有類別欄位

發布於 · 標記為 ECMAScript ES2022

多項提案擴充了現有的 JavaScript 類別語法,並新增功能。本文說明 V8 v7.2 和 Chrome 72 中的新公開類別欄位語法,以及即將推出的私有類別欄位語法。

以下是一個建立名為 IncreasingCounter 類別的實例的程式碼範例

const counter = new IncreasingCounter();
counter.value;
// logs 'Getting the current value!'
// → 0
counter.increment();
counter.value;
// logs 'Getting the current value!'
// → 1

請注意,存取 value 會在傳回結果之前執行一些程式碼(例如,記錄訊息)。現在請自問,你會如何使用 JavaScript 實作這個類別?🤔

ES2015 類別語法 #

以下說明如何使用 ES2015 類別語法實作 IncreasingCounter

class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}

類別會在原型上安裝 value getter 和 increment 方法。更有趣的是,類別有一個會建立實例屬性 _count 並將其預設值設定為 0 的建構函式。我們目前傾向使用底線開頭來表示 _count 不應由類別的使用者直接使用,但這只是一個慣例;它並非具有語言強制執行特殊語意的「私有」屬性。

const counter = new IncreasingCounter();
counter.value;
// logs 'Getting the current value!'
// → 0

// Nothing stops people from reading or messing with the
// `_count` instance property. 😢
counter._count;
// → 0
counter._count = 42;
counter.value;
// logs 'Getting the current value!'
// → 42

公開類別欄位 #

新的公開類別欄位語法讓我們可以簡化類別定義

class IncreasingCounter {
_count = 0;
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}

_count 屬性現在可以很清楚地宣告在類別的最上方。我們不再需要建構函式來定義一些欄位。太棒了!

但是,_count 欄位仍然是一個公開屬性。在這個特定的範例中,我們希望防止使用者直接存取該屬性。

私有類別欄位 #

這時就需要用到私有類別欄位了。新的私有欄位語法類似於公開欄位,但 你可以使用 # 將欄位標記為私有。你可以將 # 視為欄位名稱的一部分

class IncreasingCounter {
#count = 0;
get value() {
console.log('Getting the current value!');
return this.#count;
}
increment() {
this.#count++;
}
}

私有欄位無法在類別主體之外存取

const counter = new IncreasingCounter();
counter.#count;
// → SyntaxError
counter.#count = 42;
// → SyntaxError

公開與私有靜態屬性 #

類別欄位語法也可以用來建立公開與私有靜態屬性和方法

class FakeMath {
// `PI` is a static public property.
static PI = 22 / 7; // Close enough.

// `#totallyRandomNumber` is a static private property.
static #totallyRandomNumber = 4;

// `#computeRandomNumber` is a static private method.
static #computeRandomNumber() {
return FakeMath.#totallyRandomNumber;
}

// `random` is a static public method (ES2015 syntax)
// that consumes `#computeRandomNumber`.
static random() {
console.log('I heard you like random numbers…');
return FakeMath.#computeRandomNumber();
}
}

FakeMath.PI;
// → 3.142857142857143
FakeMath.random();
// logs 'I heard you like random numbers…'
// → 4
FakeMath.#totallyRandomNumber;
// → SyntaxError
FakeMath.#computeRandomNumber();
// → SyntaxError

更簡單的子類別化 #

當處理會新增欄位的子類別時,類別欄位語法的優點會變得更明顯。想像下列基本類別Animal

class Animal {
constructor(name) {
this.name = name;
}
}

若要建立會新增其他執行個體屬性的Cat子類別,您以前必須呼叫super()來執行Animal基本類別的建構函式,才能建立屬性

class Cat extends Animal {
constructor(name) {
super(name);
this.likesBaths = false;
}
meow() {
console.log('Meow!');
}
}

這只是為了表示貓咪不喜歡洗澡,卻需要這麼多樣板程式碼。幸運的是,類別欄位語法消除了對整個建構函式的需求,包括令人尷尬的super()呼叫

class Cat extends Animal {
likesBaths = false;
meow() {
console.log('Meow!');
}
}

功能支援 #

支援公開類別欄位 #

支援私人類別欄位 #

支援私人方法和存取器 #