JavaScript/class

カテゴリ:Book:JavaScript#class%20

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 クラスを定義し、nameage のプロパティを持つインスタンスを作成しています。また、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における「フィールド」とは、クラスのインスタンスが保持するプロパティ(値)を指します。クラスの内部で定義された変数やデータを、インスタンス(オブジェクト)ごとに管理するために使われます。フィールドはクラスのコンストラクタ内で定義されることが多いですが、クラス外部でも直接設定できるプロパティもあります。

フィールドの種類

  1. インスタンスフィールド(通常のフィールド)
    これらはクラスのインスタンスごとに持たれるプロパティです。通常、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.namethis.age はインスタンスフィールドです。
    • 各インスタンスごとに異なる値を持ちます。
  2. 静的フィールド(クラスフィールド)
    静的フィールドはクラス全体で共有されるプロパティです。インスタンスではなく、クラス自体にバインドされます。ES2022(ES13)から正式にサポートされています。
    class Person {
      static species = 'Homo sapiens';  // 静的フィールド
    }
    
    console.log(Person.species); // Homo sapiens
    
    • 静的フィールドはclassName.propertyの形でアクセスします。
    • 静的フィールドはインスタンスには存在せず、クラスそのものに紐づいています。
  3. プライベートフィールド
    プライベートフィールドは#記号で始まる変数で、クラスの外部から直接アクセスできないフィールドです。クラス内のメソッドからのみアクセス可能です。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など)を通じて間接的にアクセスできます。

フィールドの定義方法

  1. インスタンスフィールドの定義(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
    
  2. 静的フィールドの定義
    クラス自体に関連付けられたフィールドは、staticキーワードを使って定義します。
    class Cat {
      static species = 'Felis catus';  // 静的フィールド
    
      constructor(name) {
        this.name = name;  // インスタンスフィールド
      }
    }
    
    console.log(Cat.species); // Felis catus
    
  3. プライベートフィールドの定義
    プライベートフィールドは#記号を使って定義します。
    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のクラスにおけるgetsetは、プロパティのアクセスや設定をカスタマイズするために使用される アクセサメソッド です。これにより、オブジェクトのプロパティに対する読み取りや書き込み時に特定の処理を行うことができます。

基本的な使い方

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 (変更なし)

解説

  1. ゲッター(get:
    • getメソッドは、プロパティへのアクセスをカスタマイズします。上記の例では、nameを取得するためにget name()を定義しています。
    • ゲッターは、person.nameのようにプロパティとしてアクセスされますが、実際にはメソッドが呼び出されています。
  2. セッター(set:
    • setメソッドは、プロパティに新しい値を設定する際に呼び出されます。上記の例では、nameageの設定に制限を設けています。
    • セッターは、person.name = 'Bob'のようにプロパティに新しい値を代入することで呼び出されます。

ポイント

  • ゲッターとセッターは、直接プロパティにアクセスしているように見えますが、実際にはメソッドを通じて値の取得や設定が行われます。
  • セッターは、値が設定される前に検証や変換を行うのに便利です。

プライベートフィールドとゲッターおよびセッターの組み合わせ

プライベートフィールドとゲッターおよびセッターの組み合わせは非常に有意義です。これにより、クラスの内部状態を安全に管理しつつ、外部からのアクセスや変更をコントロールできます。プライベートフィールドを使うことで、直接的なプロパティのアクセスを防ぎ、ゲッターとセッターを通じて制御された方法でデータの取得や設定を行うことができます。

利点:

  1. カプセル化:
    プライベートフィールドを使用すると、クラスの内部のデータを外部から直接アクセスできないように保護できます。これにより、データの不正な変更や不適切なアクセスを防ぐことができます
  2. 制御されたアクセス:
    ゲッターやセッターを使用すると、外部からプロパティにアクセスする際に、条件を追加したり、変換を行ったりすることができます。たとえば、年齢がマイナスにならないように制限を加えたり、値を設定する際にバリデーションを行ったりできます。
  3. 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 を使用して他のクラスを継承可能 関数の継承はプロトタイプチェーンを使用

関連項目

参考

カテゴリ:JavaScript
カテゴリ:Book:JavaScript カテゴリ:JavaScript カテゴリ:Pages using the JsonConfig extension