JavaScriptのクラス構文は、インスタンスに属するメンバー、プロトタイプに属するメンバー、そしてクラス自体に属するメンバーを明確に定義することを可能にします。
インスタンスメンバー
クラスコンストラクタがnew演算子で呼び出されるたびに、新しく作成されたインスタンス(this)に独自のプロパティが追加されます。これらのプロパティはインスタンスごとに一意であり、プロトタイプを通じて共有されることはありません。
class Person {
constructor() {
// 各インスタンスに固有のIDを割り当て
this.id = Math.random().toString(36).substr(2, 9);
// インスタンスメソッドを定義
this.displayId = function() {
console.log(`Instance ID: ${this.id}`);
};
// インスタンスごとのデータ配列
this.tags = ['user', 'active'];
}
}
const userA = new Person();
const userB = new Person();
userA.displayId(); // Instance ID: (ランダムなID)
userB.displayId(); // Instance ID: (異なるランダムなID)
console.log(userA.id === userB.id); // false
console.log(userA.displayId === userB.displayId); // false
console.log(userA.tags === userB.tags); // false
// 各インスタンスは独立して変更可能
userA.tags.push('admin');
userB.tags.push('guest');
console.log(userA.tags); // ['user', 'active', 'admin']
console.log(userB.tags); // ['user', 'active', 'guest']
プロトタイプメソッドとアクセサ
クラスブロック内で定義されたメソッドは、自動的にプロトタイプに配置され、すべてのインスタンス間で共有されます。これにより、メモリ効率が向上し、インスタンス間での一貫した動作が保証されます。
class Entity {
constructor() {
// インスタンス固有のメソッド(プロトタイプメソッドを上書き)
this.identify = () => console.log('instance method');
}
// プロトタイプメソッド
identify() {
console.log('prototype method');
}
// 計算されたプロパティ名も使用可能
['get' + 'Type']() {
console.log('computed method name');
}
}
const obj = new Entity();
obj.identify(); // instance method
Entity.prototype.identify(); // prototype method
obj.getType(); // computed method name
クラス定義では、ゲッターとセッターもサポートされています。これらはプロトタイプ上に定義され、プロパティの値を取得・設定する際にカスタムロジックを実行できます。
class Temperature {
constructor(celsius = 0) {
this._celsius = celsius;
}
set celsius(value) {
if (typeof value !== 'number') {
throw new TypeError('Temperature must be a number');
}
this._celsius = value;
}
get celsius() {
return this._celsius;
}
get fahrenheit() {
return (this._celsius * 9/5) + 32;
}
set fahrenheit(value) {
this._celsius = (value - 32) * 5/9;
}
}
const temp = new Temperature(25);
console.log(temp.celsius); // 25
console.log(temp.fahrenheit); // 77
temp.fahrenheit = 86;
console.log(temp.celsius); // 30
静的クラスメンバー
staticキーワードを使用して定義された静的メンバーは、クラス自体に属し、インスタンスではなくクラスから直接呼び出されます。静的メソッド内のthisはクラス自体を参照します。
class MathUtil {
static PI = 3.14159;
static circleArea(radius) {
return this.PI * radius * radius;
}
constructor() {
// インスタンスメソッド
this.calculate = () => console.log('instance calculation');
}
// プロトタイプメソッド
calculate() {
console.log('prototype calculation');
}
// 静的メソッド
static calculate() {
console.log('static calculation');
}
}
const calc = new MathUtil();
calc.calculate(); // instance calculation
MathUtil.prototype.calculate(); // prototype calculation
MathUtil.calculate(); // static calculation
// 静的メンバーの実用例:ファクトリメソッド
class User {
constructor(name, role) {
this.name = name;
this.role = role;
}
static createAdmin(name) {
return new User(name, 'admin');
}
static createGuest() {
return new User('Guest', 'guest');
}
}
const admin = User.createAdmin('Alice');
const guest = User.createGuest();
console.log(admin); // User { name: 'Alice', role: 'admin' }
console.log(guest); // User { name: 'Guest', role: 'guest' }
プロトタイプとクラスへのデータメンバーの追加
クラス構文ではプロトタイプやクラスに直接データプロパティを定義することはできませんが、クラス定義の外部で手動追加することは可能です。
class Logger {
constructor() {
this.logs = [];
}
log(message) {
this.logs.push(`${Logger.timestamp}: ${message}`);
}
getLogs() {
return this.logs;
}
}
// クラスに静的プロパティを追加
Logger.timestamp = new Date().toISOString();
// プロトタイプにプロパティを追加
Logger.prototype.version = '1.0.0';
const logger = new Logger();
logger.log('Application started');
logger.log('User logged in');
console.log(logger.getLogs());
// ["2023-...: Application started", "2023-...: User logged in"]
console.log(logger.version); // 1.0.0
console.log(Logger.timestamp); // (現在のISOタイムスタンプ)