JavaScriptクラスにおけるインスタンス、プロトタイプ、および静的メンバー

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タイムスタンプ)

タグ: javascript ES6 class OOP prototype

7月29日 23:40 投稿