JavaScript/class
class
class は、JavaScript におけるオブジェクト指向プログラミングのための構文です。クラスはオブジェクトを作成するためのテンプレートであり、プロパティやメソッドを定義することができます。ES6(ECMAScript 2015)で導入され、従来のプロトタイプベースの継承よりも簡潔にオブジェクト指向を表現できます。
構文
class MyClass { constructor(name) { this.name = name; } greet() { console.log(`Hello, ${this.name}!`); } }
説明
class
は、オブジェクトのプロパティやメソッドを定義するためのテンプレートです。- クラスは、インスタンス化することでオブジェクトを作成できます。
constructor
メソッドは、クラスのインスタンスを作成する際に呼び出され、オブジェクトの初期化を行います。- クラス内で定義されたメソッドは、そのクラスのインスタンスからアクセスできます。
使用例
// クラスの定義 class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`); } } // クラスのインスタンスを作成 const person1 = new Person('Alice', 30); person1.greet(); // "Hello, my name is Alice and I am 30 years old." const person2 = new Person('Bob', 25); person2.greet(); // "Hello, my name is Bob and I am 25 years old."
この例では、Person
クラスを定義し、name
と age
のプロパティを持つインスタンスを作成しています。また、greet
メソッドを使用して、インスタンスの情報を表示しています。
継承
クラスは他のクラスを継承して、新しいクラスを作成することができます。継承を使用することで、共通の機能を親クラスから子クラスに引き継ぐことができます。
// 継承の例 class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a sound.`); } } class Dog extends Animal { constructor(name, breed) { super(name); // 親クラスのコンストラクタを呼び出し this.breed = breed; } speak() { console.log(`${this.name} barks.`); } } const dog = new Dog('Rex', 'Golden Retriever'); dog.speak(); // "Rex barks."
この例では、Dog
クラスが Animal
クラスを継承しており、speak
メソッドをオーバーライドしています。super
キーワードを使って、親クラスのコンストラクタを呼び出しています。
注意点
- クラス内で定義されたメソッドは、インスタンスを通じて呼び出されます。クラス自体では直接呼び出すことはできません。
- クラス名は通常、
PascalCase
(最初の文字が大文字)で記述します。 - クラスのメソッド内で
this
を使用すると、そのクラスのインスタンスを指します。
フィールド
JavaScriptのclass
における「フィールド」とは、クラスのインスタンスが保持するプロパティ(値)を指します。クラスの内部で定義された変数やデータを、インスタンス(オブジェクト)ごとに管理するために使われます。フィールドはクラスのコンストラクタ内で定義されることが多いですが、クラス外部でも直接設定できるプロパティもあります。
フィールドの種類
- インスタンスフィールド(通常のフィールド)
- これらはクラスのインスタンスごとに持たれるプロパティです。通常、
constructor
内でthis
を使って設定します。 class Person { constructor(name, age) { this.name = name; // インスタンスフィールド this.age = age; // インスタンスフィールド } } const person1 = new Person('Alice', 30); console.log(person1.name); // Alice console.log(person1.age); // 30
this.name
とthis.age
はインスタンスフィールドです。- 各インスタンスごとに異なる値を持ちます。
- これらはクラスのインスタンスごとに持たれるプロパティです。通常、
- 静的フィールド(クラスフィールド)
- 静的フィールドはクラス全体で共有されるプロパティです。インスタンスではなく、クラス自体にバインドされます。ES2022(ES13)から正式にサポートされています。
class Person { static species = 'Homo sapiens'; // 静的フィールド } console.log(Person.species); // Homo sapiens
- 静的フィールドは
className.property
の形でアクセスします。 - 静的フィールドはインスタンスには存在せず、クラスそのものに紐づいています。
- プライベートフィールド
- プライベートフィールドは
#
記号で始まる変数で、クラスの外部から直接アクセスできないフィールドです。クラス内のメソッドからのみアクセス可能です。ES2022で追加されました。 class Person { #name; // プライベートフィールド constructor(name) { this.#name = name; } getName() { return this.#name; // プライベートフィールドへのアクセス } } const person1 = new Person('Alice'); console.log(person1.getName()); // Alice console.log(person1.#name); // エラー: プライベートフィールドへのアクセスは無効
#name
はプライベートフィールドで、クラス外からはアクセスできません。- メソッド(
getName
など)を通じて間接的にアクセスできます。
- プライベートフィールドは
フィールドの定義方法
- インスタンスフィールドの定義(
this
を使用)- コンストラクタ内で
this
を使ってインスタンスごとのプロパティを定義します。 class Dog { constructor(name, breed) { this.name = name; // インスタンスフィールド this.breed = breed; // インスタンスフィールド } } const dog = new Dog('Rex', 'German Shepherd'); console.log(dog.name); // Rex console.log(dog.breed); // German Shepherd
- コンストラクタ内で
- 静的フィールドの定義
- クラス自体に関連付けられたフィールドは、
static
キーワードを使って定義します。 class Cat { static species = 'Felis catus'; // 静的フィールド constructor(name) { this.name = name; // インスタンスフィールド } } console.log(Cat.species); // Felis catus
- クラス自体に関連付けられたフィールドは、
- プライベートフィールドの定義
- プライベートフィールドは
#
記号を使って定義します。 class Car { #speed; // プライベートフィールド constructor() { this.#speed = 0; } accelerate() { this.#speed += 10; } getSpeed() { return this.#speed; } } const car = new Car(); car.accelerate(); console.log(car.getSpeed()); // 10 console.log(car.#speed); // エラー: プライベートフィールドへのアクセスは無効
- プライベートフィールドは
まとめ
- インスタンスフィールド: 各インスタンスに関連するプロパティ。
constructor
内でthis
を使って定義します。 - 静的フィールド: クラス全体で共有されるプロパティ。
static
キーワードを使って定義します。 - プライベートフィールド: クラス外からアクセスできないプロパティ。
#
記号を使って定義します。
フィールドを適切に使い分けることで、クラスの設計がより柔軟で安全になります。特に、プライベートフィールドとゲッター・セッターを組み合わせることで、内部のデータを外部から制御された方法でアクセスできるようになります。
アクセサメソッド
JavaScriptのクラスにおけるget
とset
は、プロパティのアクセスや設定をカスタマイズするために使用される アクセサメソッド です。これにより、オブジェクトのプロパティに対する読み取りや書き込み時に特定の処理を行うことができます。
基本的な使い方
get
(ゲッター)
get
はプロパティの値を取得する際に使用され、クラス内で定義されたメソッドとして機能します。
set
(セッター)
set
はプロパティの値を設定する際に使用され、値が設定される前に処理を行うことができます。
例:
class Person { constructor(name, age) { this._name = name; this._age = age; } // ゲッター: nameを取得する get name() { return this._name; } // セッター: nameを設定する set name(value) { if (value.length < 3) { console.log('名前は3文字以上でなければなりません'); } else { this._name = value; } } // ゲッター: ageを取得する get age() { return this._age; } // セッター: ageを設定する set age(value) { if (value < 0 || value > 120) { console.log('年齢は0から120の範囲でなければなりません'); } else { this._age = value; } } } const person = new Person('Alice', 30); console.log(person.name); // Alice (ゲッターの呼び出し) person.name = 'Bob'; // セッターを通じて名前を変更 console.log(person.name); // Bob person.age = 150; // セッターが年齢制限を適用 console.log(person.age); // 30 (変更なし)
解説
- ゲッター(
get
):get
メソッドは、プロパティへのアクセスをカスタマイズします。上記の例では、name
を取得するためにget name()
を定義しています。- ゲッターは、
person.name
のようにプロパティとしてアクセスされますが、実際にはメソッドが呼び出されています。
- セッター(
set
):set
メソッドは、プロパティに新しい値を設定する際に呼び出されます。上記の例では、name
とage
の設定に制限を設けています。- セッターは、
person.name = 'Bob'
のようにプロパティに新しい値を代入することで呼び出されます。
ポイント
- ゲッターとセッターは、直接プロパティにアクセスしているように見えますが、実際にはメソッドを通じて値の取得や設定が行われます。
- セッターは、値が設定される前に検証や変換を行うのに便利です。
プライベートフィールドとゲッターおよびセッターの組み合わせ
プライベートフィールドとゲッターおよびセッターの組み合わせは非常に有意義です。これにより、クラスの内部状態を安全に管理しつつ、外部からのアクセスや変更をコントロールできます。プライベートフィールドを使うことで、直接的なプロパティのアクセスを防ぎ、ゲッターとセッターを通じて制御された方法でデータの取得や設定を行うことができます。
利点:
- カプセル化:
- プライベートフィールドを使用すると、クラスの内部のデータを外部から直接アクセスできないように保護できます。これにより、データの不正な変更や不適切なアクセスを防ぐことができます
- 制御されたアクセス:
- ゲッターやセッターを使用すると、外部からプロパティにアクセスする際に、条件を追加したり、変換を行ったりすることができます。たとえば、年齢がマイナスにならないように制限を加えたり、値を設定する際にバリデーションを行ったりできます。
- APIの柔軟性:
- ゲッターとセッターを使用すると、クラスの内部のロジックを変更しても、外部のコードに対してその影響を最小限に抑えることができます。プロパティの取得や設定の方法を後から変更することができ、APIの変更を柔軟に対応できます。
実例:
class BankAccount { #balance; // プライベートフィールド constructor(initialBalance = 0) { this.#balance = initialBalance; } // ゲッター: 残高を取得 get balance() { return this.#balance; } // セッター: 残高を設定(引き出しの際には制限を加える) set balance(amount) { if (amount < 0) { console.log('残高は0以上でなければなりません'); } else { this.#balance = amount; } } // 入金メソッド deposit(amount) { if (amount > 0) { this.#balance += amount; } } // 引き出しメソッド withdraw(amount) { if (amount > 0 && amount <= this.#balance) { this.#balance -= amount; } else { console.log('引き出し金額が無効です'); } } } const account = new BankAccount(1000); console.log(account.balance); // 1000 (ゲッターを通じて残高を取得) account.balance = 1500; // セッターを通じて残高を変更 console.log(account.balance); // 1500 account.balance = -500; // 無効な値が設定された場合、エラーメッセージ console.log(account.balance); // 1500 (変更なし)
この例の解説:
#balance
はプライベートフィールドとして定義されており、外部から直接アクセスできません。- ゲッター (
get balance()
) は#balance
を返し、外部から安全に取得できます。 - セッター (
set balance()
) では、指定された値が負の値でないことを確認するバリデーションを行っています。無効な値が設定されるのを防ぎます。 deposit()
とwithdraw()
メソッドでは、残高の更新に対して追加のチェックを行っており、データの整合性を保っています。
まとめ
プライベートフィールドとゲッター、セッターの組み合わせは、クラスの内部状態を適切に管理し、外部からのアクセスを制御するための強力なツールです。これにより、クラスのデータに対して一貫性のあるルールやバリデーションを適用でき、より堅牢でメンテナンスしやすいコードを実現できます。
クラスと関数の違い
特徴 | class | 関数 |
---|---|---|
定義 | class キーワードを使って定義 |
function キーワードを使って定義 |
インスタンス化 | new キーワードを使ってインスタンス化 |
呼び出し時に即実行 |
継承 | extends を使用して他のクラスを継承可能 |
関数の継承はプロトタイプチェーンを使用 |