PR

Javascript:クラスとは何か?を3分で【初心者向け】

JavaScript

クラスとは、オブジェクト指向プログラミング(OOP)の中心的な概念で、属性(プロパティ)と動作(メソッド)を1つにまとめた設計図のようなものです。クラスを使用することで、コードの再利用性が高まり、管理や保守が容易に。クラスは具体的なオブジェクト(インスタンス)を生成するための「型」として機能します。

Javascript クラス
図1:クラスの概念

以下のJavaScriptコードは、Carというクラスを定義し、それを使用して具体的な車オブジェクトを作成する例です。

class Car {
  constructor(brand, model) {
    this.brand = brand;
    this.model = model;
  }

  displayInfo() {
    console.log(`This car is a ${this.brand} model ${this.model}.`);
  }
}

// Carクラスのインスタンスを生成
const myCar = new Car('Toyota', 'Corolla');
myCar.displayInfo(); // 出力: This car is a Toyota model Corolla.

このページでは↑のサンプルコードの意味・内容を初心者でも理解できるように、Javascriptにおけるクラスの基本を1からわかりやすくご説明します。Webエンジニア/Webデザイナーを目指す方であれば知らないと恥ずかしい超・基本知識の1つです。是非最後までご覧ください。

参考 【JavaScript入門】基本文法/基礎文法を5分で

スポンサーリンク

Javascript:クラスとは?

クラスとはオブジェクトを作成するための「設計書」のこと。

簡単な日常生活の例を使ってご説明します。

まず、クラスとは何かを理解するために、車を作る工程を思い浮かべてみてください。あなたは自動車メーカーに勤めていて、新型の車を作りたいと思ったとしましょう。その際、最初に行う工程が「設計図を作成すること」になるはずです。いきなり車の部品を作り始めるのではなく、車の「形」や「サイズ」「色」や「部品の構成」に加えて、アクセルやブレーキなど、どのような機能を持つべきかを定義する必要があるでしょう。

自動車メーカーは車を1台だけ作ればよいわけではなく、100台~10000台と製造する必要があるので、設計図を作成することは重要な作業になります。

結論を言うと「クラスとは、この設計図のようなもの」です。

どのようなデータ(属性)を持ち、どのような動作(メソッド)をするべきかを定義するのが、クラスを定義するということになります。

車の設計図の例で言えば、データ(属性)とは「車の色」「車種」「製造年」などがそれに該当します。動作(メソッド)とは「アクセルを踏む」、「ブレーキを踏む」、「ハンドルを切る」などを指します。

Javascript クラスとは
図2:クラスとは?

車の設計図の例で言えば、データ(属性)とは「車の色」「車種」「製造年」などがそれに該当します。動作(メソッド)とは「アクセルを踏む」、「ブレーキを踏む」、「ハンドルを切る」などを指します。

ひとまず、ここでは何となく「クラスってそういうものなんだな」とイメージできればOKです。で、早速Javascriptでクラスを定義する方法を見ていきましょう。

Javascript:class

Javascriptでクラスを定義するにはclassキーワードを使用します。ちなみに、クラス名の最初の文字は大文字で始めるのが一般的です。

class FirstClass {
  constructor() {
    // コンストラクタメソッド: 新しいインスタンスを作成する際に実行されます。
    // インスタンス固有の初期化処理をここで行います。
  }

  method1() {
    // メソッド1の定義: クラスのインスタンスが利用できる機能を提供します。
  }

  method2() {
    // メソッド2の定義: 別の機能を提供します。
  }
}

この例では、FirstClassという名前のクラスを定義しています。このクラスには以下のような基本的な構成要素を含みます。

  1. constructor()メソッド: クラスから新しいオブジェクトが作成されるたびに自動的に実行される特別なメソッド。このメソッド内で、そのインスタンスの初期状態を設定するためのコード(例えば、プロパティの初期値を設定するなど)を書きます。
  2. メソッドの定義: method1()method2()のように、クラス内にメソッドを定義することができます。これらのメソッドは、クラスのインスタンスが利用できる機能や振る舞いを実装します。

サンプルコード1 Dogクラスの定義

class Dog {
  constructor(name, breed) {
    this.name = name; // 犬の名前
    this.breed = breed; // 犬の種類
  }

  bark() {
    console.log(`${this.name} says woof!`);
  }
}

コンストラクタ(constructor)

コンストラクタとは、オブジェクト指向プログラミングに共通して存在する重要な概念の1つで、クラスからオブジェクト(インスタンス)を生成する際に自動的に呼び出される特別なメソッドです。コンストラクタの主な役割は、新しく作成されたオブジェクトの初期化を行うこと。これには、オブジェクトのプロパティに初期値を設定する作業が含まれます。

ポイント コンストラクタの特徴

  1. 自動的に呼び出される: インスタンスが生成される時に自動的に呼び出される。この性質により、オブジェクトが適切に初期化されていることを保証することが可能になる。
  2. 初期化の役割: オブジェクトのプロパティや状態に初期値を設定。→オブジェクトが正しく機能するための準備が整う。
  3. 直接呼び出しは不可: コンストラクタは、newキーワードを使用してクラスからオブジェクトを生成する際にのみ内部的に呼び出されます。クラスの外部から直接呼び出すことはできません。

以下は、JavaScriptにおける簡単なPersonクラスの定義と、そのコンストラクタの使用例です。

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.`);
  }
}

// Personクラスからインスタンスを生成し、コンストラクタが自動的に呼び出される
const person1 = new Person('John', 30);

// greetメソッドを呼び出して、コンストラクタによって設定された値を使用
person1.greet(); // 出力: Hello, my name is John and I am 30 years old.

参考 ${テンプレートリテラル} / メソッドの呼び出し

この例では、Personクラスのコンストラクタが、新しいPersonオブジェクトが作成される際にnameageのプロパティに初期値を設定しています。その後、greetメソッドを通じて、これらのプロパティがどのようにオブジェクト内で使用されるかを示しています。

コンストラクタを使用することで、オブジェクトが期待する状態で生成されることを保証し、プロパティの値が適切に設定されていることを確認できます。これにより、オブジェクトの安定した動作と、コードの保守性の向上が期待できます。

インスタンスの作成

Javascript クラス
(再掲)図1:クラスの概念

定義したクラスはそのままでは利用できません。インスタンスを作成したとき始めて実体化します。ここでは、インスタンス化についてご説明します。

「インスタンス化」とは、クラス(オブジェクトの設計図)をもとにして、具体的なオブジェクト(インスタンス)を生成するプロセスのことを指します。オブジェクト指向プログラミングにおいて、クラスは属性(プロパティ)と動作(メソッド)を定義するテンプレートのようなものですが、クラス自体は具体的なデータや状態を持ちません。インスタンス化を通じて、この抽象的な定義から実際に動作するオブジェクトを作り出します。

ポイント インスタンス化のプロセス

  1. クラスの定義: まず、オブジェクトの構造や振る舞いを定義するクラスを作成。
  2. インスタンスの生成: クラスから新しいインスタンスを生成するには、newキーワードを使ってクラスをインスタンス化する。このステップでコンストラクタが呼び出され、オブジェクトの初期化が行われる。
  3. インスタンスの使用: インスタンス化されたオブジェクトは、定義されたプロパティやメソッドを持ち、プログラム内で自由に使うことができる。
class FirstClass {
~~~
}

// FirstClassのインスタンスを作成
const firstInstance = new FisrtClass();

// インスタンスのメソッドを呼び出す
firstInstance.method1();
firstInstance.method2();

newキーワードを使ってFirstClassのインスタンスを作成し、その後でインスタンスに対して定義されたメソッドを呼び出しています。これにより、クラスを利用した具体的なオブジェクトが生成され、そのオブジェクト固有の機能を使用することができます。

サンプルコード2 Dogクラスのインスタンス化と呼び出し

class Dog {
  constructor(name, breed) {
    this.name = name; // 犬の名前
    this.breed = breed; // 犬の種類
  }

  bark() {
    console.log(`${this.name} says woof!`);
  }
}


// `Dog`クラスからインスタンスを作成
const myDog = new Dog("Buddy", "Golden Retriever");

// インスタンスのメソッドを呼び出す
myDog.bark(); // 出力: Buddy says woof!

インスタンスの作成にはnewキーワードを使用し、Dogクラスに定義されたbarkメソッドを呼び出しています。この方法により、オブジェクト指向プログラミングの基本的な概念である「カプセル化」「継承」「ポリモーフィズム」を活用し、効率的かつ整理されたコードを実現できます。

ここまでの説明でクラスの基本的な概念と使い方についてはアルテ色理解が深まったと思います。クラスの定義、コンストラクタの役割、インスタンス化のプロセス、そしてプロパティとメソッドの定義と使用方法。これらはオブジェクト指向プログラミングにおいて非常に重要な概念であり、JavaScriptをはじめとする多くのプログラミング言語で広く使われています。

ただし、クラスを完璧に理解するためには、以下のようなさらに高度なトピックも存在します。ここからは、↓についていくつかサンプルコードをもとに深堀して解説していきます。

概念説明
継承クラス間でコードを再利用するためのメカニズム。基底クラスのプロパティやメソッドを派生クラスが継承する。
カプセル化クラス内のデータ(プロパティ)と振る舞い(メソッド)を一つにまとめ、外部からの直接的なアクセスを制限する方法。
ポリモーフィズム異なるクラスのオブジェクトが同じインターフェースを通じて異なる振る舞いを実現する概念。
静的メソッドとプロパティインスタンスを生成せずにクラス自体に紐づけられるメソッドやプロパティ。

継承(extends)

継承とは、あるクラス(基底クラスまたは親クラスと呼ばれる)の特性(プロパティとメソッド)を別のクラス(派生クラスまたは子クラスと呼ばれる)に引き継ぐメカニズムです。継承を利用することで、既存のコードの再利用、プログラムの構造の整理、および複雑性の管理が容易になります。

JavaScriptでの継承の簡単な例を以下に示します。クラスの継承を行う際にはextendsキーワードを使用します。

// 基底クラス(親クラス)
class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a sound.`);
  }
}

// 派生クラス(子クラス) - Animalクラスを継承
class Dog extends Animal {
  constructor(name) {
    super(name); // 親クラスのコンストラクタを呼び出し
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

const dog = new Dog('Rex');
dog.speak(); // 出力: Rex barks.

↑の例では、Animalクラスが基底クラスとして定義されており、DogクラスがAnimalクラスを継承しています。DogクラスはAnimalクラスのconstructorspeakメソッドを継承していますが、speakメソッドをオーバーライド(上書き)して独自の振る舞いを定義しています。

super

クラスを継承して利用する際の「super」キーワードについてご説明します。

superは、派生クラス(子クラス)から基底クラス(親クラス)のコンストラクタやメソッドにアクセスするために使用されるキーワードです。superの使用目的は主に以下の二つに分けられます。

パターン1:コンストラクタ内でのsuper

派生クラスのコンストラクタ内でsuperを使用すると、基底クラスのコンストラクタを呼び出すことができます。これは、派生クラスで基底クラスのコンストラクタを実行して初期化処理を行う必要がある場合に使用します。派生クラスのコンストラクタ内でsuperを呼び出さないと、基底クラスのプロパティが適切に初期化されず、エラーが発生する可能性があります。

class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 基底クラスのコンストラクタを呼び出し、nameプロパティを初期化
    this.breed = breed; // 追加のプロパティを初期化
  }
}

パターン2:メソッド内でのsuper

派生クラスのメソッド内でsuperを使用すると、基底クラスに定義されたメソッドにアクセスできます。これは、派生クラスで基底クラスのメソッドの振る舞いを再利用したい、または拡張したい場合に便利です。superを用いることで、基底クラスのメソッドをオーバーライド(上書き)しつつ、その元の実装を呼び出すことが可能になります。

class Animal {
  speak() {
    console.log("Animal makes a sound.");
  }
}

class Dog extends Animal {
  speak() {
    super.speak(); // 基底クラスのspeakメソッドを呼び出し
    console.log("Dog barks.");
  }
}

const dog = new Dog();
dog.speak();
// 出力:
// Animal makes a sound.
// Dog barks.

superは派生クラスから基底クラスのコンストラクタやメソッドにアクセスし、それらを利用するための強力な機能を提供する、と理解できればOKです。

カプセル化

JavaScriptにおける「カプセル化」は、オブジェクトの詳細を外部から隠し、外部から直接アクセスできる部分を制限するオブジェクト指向プログラミングの原則の1つです。カプセル化によってオブジェクトの内部状態を保護し、外部からの不正なアクセスや予期しない変更を防ぐことができます。これにより、オブジェクトの利用者は、そのオブジェクトが提供する公開インターフェース(メソッドやプロパティ)を通じてのみオブジェクトとやり取りすることになります。

カプセル化することで、そのクラスが正常に動くために「変えてはいけない部分を変えられなくする」ことができるということです。

JavaScriptにおけるカプセル化の実現方法

JavaScriptでカプセル化を実現する方法はいくつかありますが、一般的な方法は以下の通りです。

パターン1 プライベートフィールドとメソッド(ES2020以降)

最新のJavaScriptでは、クラス内でプライベートフィールドやメソッドを定義するために#プレフィックスを使用します。これにより、クラスの外部からはアクセスできなくなります。

class Person {
  #name; // プライベートフィールド

  constructor(name) {
    this.#name = name;
  }

  getName() {
    return this.#name; // プライベートフィールドへのアクセス
  }
}

const person = new Person('John');
console.log(person.getName()); // John
console.log(person.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class

プロパティ「#name」はカプセル化されたので、外部から直接参照することはできません。そのため、最後の行で「#name」を参照している個所はエラーとなっていることが分かります。

パターン2 クロージャを使用する

クロージャを利用してプライベート変数や関数を作成することもできます。この方法は、ES2020以前から利用可能で、関数スコープを活用します。

このページで解説している「クラス」とは直接的に関係ありませんか「カプセル化」の観点で合わせて覚えておきたいテクニックです。

function Person(name) {
  let privateName = name; // プライベート変数

  this.getName = function() {
    return privateName;
  };
}

const person = new Person('John');
console.log(person.getName()); // John
console.log(person.privateName); // undefined

参考 function文 / 関数スコープとは?

privateName は関数内でのみ有効なので、これは外部から参照することはできないということです。

カプセル化の利点は「安全性: オブジェクトの内部状態が外部から直接変更されることを防ぐ」「メンテナンス性: 内部実装の変更が外部のコードに影響を与えにくくなる」「使用の単純化: 利用者はオブジェクトの公開インターフェースだけを理解すればOK(内部の複雑さを知る必要がない)」という点にあります。

カプセル化は、プログラムの堅牢性を高め、複雑性を管理するのに役立つ重要な概念です。JavaScriptではプライベートフィールドやクロージャを使った方法でカプセル化を実現し、より安全でメンテナンスしやすいコードを書くことができます。

ポリモーフィズム

ポリモーフィズム(多態性)は、異なるクラスのオブジェクトが同一のインターフェースやメソッドを通じて異なる動作をする能力を指します。ポリモーフィズムを利用することで、コードの再利用性を高め、柔軟性と拡張性を向上させることができます。

サンプル ポリモーフィズムの例

class Animal {
  speak() {
    console.log("Animal speaks");
  }
}

class Dog extends Animal {
  speak() {
    console.log("Dog barks");
  }
}

class Cat extends Animal {
  speak() {
    console.log("Cat meows");
  }
}

function makeAnimalSpeak(animal) {
  animal.speak(); // 実行時にオブジェクトの実際のクラスに基づいて適切なメソッドが呼び出される
}

const dog = new Dog();
const cat = new Cat();

makeAnimalSpeak(dog); // 出力: Dog barks
makeAnimalSpeak(cat); // 出力: Cat meows

↑の例では、Animalクラスを継承したDogクラスとCatクラスが異なるspeakメソッドの実装を持っています。makeAnimalSpeak関数はAnimal型のパラメータを取りますが、実際にはそのサブクラスのインスタンスを受け取り、オーバーライドされたメソッドを実行します。これにより、異なるクラスのオブジェクトが同一のインターフェース(この場合はspeakメソッド)を通じて異なる動作を示すポリモーフィズムが実現されています。

ポリモーフィズムの主な形態

  1. サブタイプポリモーフィズム(継承ポリモーフィズム)
    • サブタイプポリモーフィズムは、基底クラス(親クラス)の参照を通じて派生クラス(子クラス)のインスタンスを扱うことができる性質です。派生クラスは基底クラスのメソッドをオーバーライド(上書き)することで、異なる振る舞いを実現します。
  2. アドホックポリモーフィズム(オーバーロードポリモーフィズム)
    • 同じ関数名でも、引数の型や数に応じて異なる処理を行うことを指します。JavaScriptでは関数のオーバーロードは直接的にはサポートされていませんが、引数の数や型をチェックすることで同様の動作を実現できます。

ポリモーフィズムを利用することで、プログラムの柔軟性を高め、異なるクラスのオブジェクトを一つのインターフェースを通じて扱うことが可能になります。これは、特に大規模なアプリケーションやライブラリの設計において、コードの簡潔さと再利用性の向上に寄与します。

静的メソッドとプロパティ

静的メソッドとプロパティは、クラスのインスタンスではなくクラス自体に属するメソッドとプロパティです。これらはクラスのインスタンスを生成することなく直接クラスからアクセスすることができます。静的メソッドとプロパティは、インスタンスに依存しない操作を行いたい場合や、クラスレベルで共有するデータを持つ場合に便利です。

静的メソッド

  • 静的メソッドは、クラスのインスタンス化なしに呼び出すことができるメソッド。
  • staticキーワードを使ってクラス内で定義されます。
  • インスタンス固有のデータ(プロパティ)にはアクセスできませんが、静的プロパティにはアクセス可能です。
  • ユーティリティ関数やファクトリーメソッドなど、インスタンスデータに依存しない処理を実装するのに適しています。

静的プロパティ

  • 静的プロパティは、クラスのインスタンス間で共有されるクラスレベルのデータです。
  • staticキーワードを用いて定義され、クラス名を通じてアクセスします。
  • すべてのインスタンスに共通の値を保持したい場合や、設定値などをクラスに紐づけて管理したい場合に使用されます。
class MyClass {
  static staticProperty = 'クラスレベルのプロパティ';

  static staticMethod() {
    return '静的メソッドが呼び出されました';
  }
}

console.log(MyClass.staticProperty); // クラスレベルのプロパティ
console.log(MyClass.staticMethod()); // 静的メソッドが呼び出されました

この例では、MyClassに静的プロパティと静的メソッドを定義しています。これらはMyClassのインスタンスを生成せずに直接アクセスすることができます。

静的メソッドとプロパティは、インスタンスに依存しない操作を行いたい場合や、クラス全体で共有する情報を管理する場合に特に有用です。しかし、インスタンス固有のデータにアクセスする必要がある場合は、非静的メソッドやプロパティを使用する必要があります。

まとめ Javascriptのクラス

  1. クラスの定義
    • classキーワードを使用してクラスを定義。コンストラクタ内でインスタンスの初期化を行い、メソッドで振る舞いを定義。
  2. 継承
    • extendsキーワードを用いてクラス間でコードを再利用し、新しいクラスを作成。
  3. カプセル化
    • プロパティやメソッドのアクセス制限を通じて、クラスの詳細を隠蔽すること。
  4. ポリモーフィズム
    • 異なるクラスが同一のインターフェース(メソッド)を通じて異なる振る舞いを示すこと。
  5. 静的メソッドとプロパティ
    • インスタンスではなくクラス自体に属するメソッドやプロパティで、staticキーワードを用いて定義します。
class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }

  static classification() {
    console.log('Animals are living beings.');
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name);
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

const dog = new Dog('Rex');
dog.speak(); // 出力: Rex barks.
Animal.classification(); // 出力: Animals are living beings.

このWebサイトは現役のエンジニアが以下3点を目的として運営しています。

  1. 勉強:一度理解した内容を忘れないように。
    → アウトプットは「最強のインプット」である! 
  2. 備忘:忘れたとしても後から見返せるように。
    → 未来の自分への「お手紙」を書いています。 
  3. 共有:〇〇ってこうだったんだ!の感動をシェアできるように。
    → あなたの知識は誰かにとっての「価値ある情報」です。 

副業ブログの始め方はこちらから

スポンサーリンク
JavaScriptWEBデザイン
シェアする
ビズドットオンラインをフォローする
タイトルとURLをコピーしました