はじめに
JavaScript のクラス構文( class )は ES2015 で導入されましたが、その後も改良が続き、現在では private フィールド や static フィールド など、「本物のクラスらしい機能」が揃っています。
本章では、次のポイントを中心に、モダンなクラスの使い方 を整理します。
constructor(初期化処理)- メソッド(プロトタイプに登録される)
privateフィールド(インスタンスの内部状態)staticフィールド / メソッド(クラス共通の状態)- setter / getter メソッド
- クラス継承
- インスタンス生成
クラス構文の基本
class User {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, ${this.name}`);
}
}
const u = new User("Kon");
u.sayHello();
ポイント:
constructorはインスタンス生成時に自動で呼ばれる。- メソッドは
functionを書かずに定義できる。 - メソッドは プロトタイプに登録される。(メモリ効率が良い)
constructor の役割
constructor は インスタンスの初期化処理を書く場所 です。
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
constructor の注意点
returnを書かない。
→constructor に return を書くと、プリミティブを返しても無視され、オブジェクトを返した場合のみ 返り値が置き換わるという意図しない挙動になります。
- 非同期処理は基本的に書かない。
→ インスタンス生成が「途中で止まる」状態を作りやすいため
メソッドの定義
クラス内で定義したメソッドは、すべて プロトタイプメソッド になります。
class User {
sayHello() {
console.log("Hello");
}
}
なぜプロトタイプに登録されるのか?
- 全インスタンスで共有されるためメモリ効率が良い
- JavaScript のオブジェクトモデルに沿っている
アロー関数をメソッドに使うべきか?
class User {
sayHello = () => { ... } // ← これは「インスタンスごとに生成される」
}
👉 アロー関数は「メソッド」ではなく「インスタンスプロパティ」である。
| 理由 | 内容 |
|---|---|
| プロトタイプに登録されない | インスタンス毎に関数が生成される |
| this が固定されすぎる | 柔軟性が失われ、再利用性が下がる |
| メソッドとして扱えない | 継承・super・プロトタイプチェーンの恩恵を受けられない |
this の束縛は安定するが、メモリ効率が悪くなるため、通常のメソッドで十分です。
private フィールド(#field)
以前の JavaScript では、private を実現するにはクロージャを使う必要がありました。しかし、クロージャはメモリ効率が悪く、プロトタイプメソッドから参照できないという欠点がありました。
ES2022 の private フィールドは、インスタンスの内部状態を隠すための構文として、これを正式に解決した仕組みです。
class Counter {
#count = 0;
increment() {
this.#count++;
}
get value() {
return this.#count;
}
}
特徴
#nameの形式で宣言する。- インスタンス毎に存在する。
- 外部からアクセスできない。
- クラス内のどのメソッドからも参照できる。
従来、プロトタイプ型のクラスで用いられてきた let での定義は、class 直下では行えません。その為、 private の代わりにはなりません。
よくある誤解:
class User {
let secret = 123; // ❌ エラー
constructor() {
secret = 123; // ← これは constructor のローカル変数
}
show() {
console.log(secret); // ❌ エラー
}
}
👉 class の直下に書けるのは「フィールド宣言」だけであり、let / const / var は書けない(文法エラー)。
つまり、クラス変数としてもインスタンス変数としても使えない。
class User {
constructor() {
let secret = 123; // ← これは constructor のローカル変数
}
show() {
console.log(secret); // ❌ エラー
}
}
👉 let で定義された変数は、 constructor の中だけで存在するローカル変数です。インスタンスに保存されない為、他のメソッドから参照できません。
つまり、
let/constは “関数スコープ”privateは “インスタンススコープ”
という決定的な違いがあります。
注意すること:
private フィールドは、 class 直下で宣言が必須となります。
class User {
#secret; // ← 必ず必要
constructor() {
this.#secret = 123; // OK
}
}
宣言しないと構文エラーになります。👇
class User {
constructor() {
this.#secret = 123; // ❌ エラー:#secret が宣言されていない
}
}
👉 private は構文レベルで管理される特別なフィールド だからです。
一方、public は宣言不要(constructor だけで作れる)です。
class User {
constructor() {
this.name = "Kon"; // OK(public)
}
}
👇 public フィールドは、ただのオブジェクトのプロパティだからです。動的言語の JavaScript では、プロパティはいつでも追加できます。
private メソッド
内部処理を隠したい場合(外部に公開したくない場合)は private メソッドを使います。
class PasswordValidator {
validate(pw) {
return this.#checkLength(pw) && this.#checkSymbol(pw);
}
#checkLength(pw) { return pw.length >= 8; }
#checkSymbol(pw) { return /[!@#$%^&*]/.test(pw); }
}
👉 外部に公開したいのは validate() だけ、内部の実装は自由に変更できるように private にします。
static フィールド
クラス変数を定義したい場合は、 static フィールドを使用します。
class User {
static count = 0;
constructor() {
User.count++;
}
}
console.log(User.count); // 1
特徴
- クラスに属する変数
- 全インスタンスで共有される。
- アクセスは
User.countのようにクラス名を用いて行う。
よくある誤解:
static フィールドは クラス自身のプロパティ です。インスタンスからはアクセスできません。
class User {
static count = 0;
constructor() {
User.count++;
}
}
const u = new User();
u.count; // undefined
注意すること:
static フィールドは、class 直下で宣言する必要があります。
class User {
static secret; // ← static フィールドとして宣言
constructor() {
User.secret = 123; // OK(static フィールドへの代入)
}
}
class 直下で、static フィールドを宣言しない場合、エラーにはなりませんが、それは static フィールドではなく、単なるクラスの動的プロパティになります。
class User {
constructor() {
User.secret = 123; // ← 動的プロパティの追加(static フィールドではない)
}
}
※ 見た目は似ていますが、static フィールドと動的プロパティは別物 です。
class User1 {
static secret; // ← static フィールドとして宣言
constructor() {
User1.secret = 123; // OK(static フィールドへの代入)
}
}
class User2 {
constructor() {
User2.secret = 123; // ← 動的プロパティの追加(static フィールドではない)
}
}
console.log(Object.hasOwn(User1, "secret")); // true
console.log(Object.hasOwn(User2, "secret")); // false
👉 インスタンス生成後は、constructor の実行により、同じようにみえてしまう為、非常にみつけづらいバグになる可能性があります。
static メソッド
クラスメソッドを定義したい場合は、static メソッドを使用します。
class MathUtil {
static clamp(value, min, max) {
return Math.min(max, Math.max(min, value));
}
}
MathUtil.clamp(10, 0, 5); // → 5
用途:
- ユーティリティ関数
- インスタンスを数える
- 設定値を保持する
- ID 発行
- バリデーション
- インスタンス生成の補助(factoryメソッドなど)
(つづく)