パブリックおよびプライベートクラスフィールド

公開日 · タグ: 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 ゲッターと 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!');
}
}

機能サポート #

パブリッククラスフィールドのサポート #

プライベートクラスフィールドのサポート #

プライベートメソッドとアクセサーのサポート #