●JavaScriptのクラスとインスタンスとは
プログラミング初心者の方にとって、クラスやインスタンスという言葉は少し難しく感じるかもしれません。
そこで、これからJavaScriptにおけるクラスとインスタンスの概念について、わかりやすく丁寧に解説していきますので、一緒に理解を深めていきましょう。
クラスとインスタンスは、オブジェクト指向プログラミングの重要な概念です。
オブジェクト指向は、現実世界の物事をプログラムに反映させる考え方で、コードの再利用性や保守性を高めることができます。
JavaScriptでもオブジェクト指向の機能が用意されており、クラスを定義してインスタンスを生成することが可能です。
クラスは、オブジェクトの設計図や雛形のようなものだと考えてください。例えば、「車」というクラスがあるとします。
車クラスには、車の特徴である「色」や「速度」などの情報(プロパティ)や、「加速する」「停止する」などの動作(メソッド)が定義されています。
一方、インスタンスは、クラスから生成される具体的な個々のオブジェクトを指します。
「赤い車」や「青い車」は、車クラスから生成されたインスタンスと言えます。
クラスは1つですが、そこから生成されるインスタンスは複数存在できます。
各インスタンスは、クラスで定義されたプロパティやメソッドを持ちながらも、それぞれ独立した存在として扱われます。
例えば、赤い車と青い車は同じ車クラスから生成されていても、色や速度の値は個別に設定や変更が可能です。
このように、クラスとインスタンスを使うことで、同じ種類のオブジェクトを効率的に扱うことができます。
コードの重複を減らし、構造化されたプログラムを書くことが可能になります。
これからサンプルコードを交えながら、JavaScriptでのクラスとインスタンスの使い方を具体的に見ていきましょう。
○クラスとインスタンスの違い
クラスとインスタンスの違いを理解することは、オブジェクト指向プログラミングを習得する上で非常に重要です。
クラスは、オブジェクトの設計図や雛形であり、オブジェクトがどのようなプロパティやメソッドを持つのかを定義します。
クラス自体は抽象的な概念であり、実際に使用されるのはクラスから生成されたインスタンスです。
一方、インスタンスは、クラスから生成される具体的な個々のオブジェクトです。
インスタンスは、クラスで定義されたプロパティやメソッドを持ち、それぞれが独立した存在として扱われます。
例えば、「人間」というクラスがあるとします。
人間クラスには、「名前」や「年齢」などのプロパティと、「歩く」や「話す」などのメソッドが定義されています。
そして、「太郎」や「花子」は、人間クラスから生成されたインスタンスです。
太郎と花子は、それぞれ固有の名前や年齢を持っていますが、どちらも人間クラスの特徴である歩くことや話すことができます。
つまり、クラスは設計図であり、インスタンスはその設計図に基づいて実際に生成されたオブジェクトということができます。
クラスは1つですが、そこから生成されるインスタンスは複数存在できるのです。
JavaScriptでは、クラスを定義するためにclassキーワードを使用します。次のサンプルコードで、クラスの定義方法を見ていきましょう。
○サンプルコード1:クラスの定義
JavaScriptでクラスを定義するには、classキーワードを使用します。
クラス名は通常、大文字で始めるのが慣例です。
クラス内には、コンストラクタ関数とメソッドを定義します。
このコードでは、Personというクラスを定義しています。
constructorはコンストラクタ関数で、インスタンス生成時に呼び出されます。
コンストラクタ内で、thisキーワードを使ってインスタンスのプロパティ(nameとage)を初期化しています。
sayHelloは、Personクラスのメソッドです。
このメソッドでは、インスタンスのnameプロパティを使って、挨拶文を出力しています。
クラスを定義することで、同じ構造を持つオブジェクトを簡単に作成できるようになります。
このクラスからインスタンスを生成する方法は、次のサンプルコードで説明します。
○サンプルコード2:インスタンスの生成
クラスからインスタンスを生成するには、newキーワードを使用します。
newの後にクラス名を指定し、必要な引数を渡すことでインスタンスが生成されます。
このコードでは、Personクラスからtaroとhanakoの2つのインスタンスを生成しています。
new Person("太郎", 25)のように、コンストラクタに名前と年齢を引数として渡しています。
生成されたインスタンスは、それぞれ独立した存在として扱われます。
taro.sayHello()とhanako.sayHello()を呼び出すと、それぞれのインスタンスの名前が挨拶文に反映されて出力されます。
また、インスタンスのプロパティにアクセスすることもできます。
taro.ageとhanako.ageを出力すると、それぞれのインスタンスの年齢が表示されます。
●newキーワードとコンストラクタの役割
JavaScriptでインスタンスを生成する際に重要な役割を果たすのが、newキーワードとコンストラクタです。
これらを理解することで、クラスからインスタンスを適切に生成し、初期化することができるようになります。
newキーワードは、クラスからインスタンスを生成するために使用します。
newの後にクラス名を指定すると、そのクラスのインスタンスが新しく作成されます。
この際、クラス内で定義されたコンストラクタが呼び出され、インスタンスの初期化処理が行われます。
コンストラクタは、クラス内でconstructorという名前で定義される特別なメソッドです。
インスタンス生成時に自動的に呼び出され、インスタンスのプロパティを初期化するための処理を記述します。
コンストラクタには、インスタンス生成時に渡された引数を受け取ることができ、それらの値を使ってプロパティを設定することが一般的です。
サンプルコードを見ながら、newキーワードとコンストラクタの使い方を詳しく見ていきましょう。
○サンプルコード3:コンストラクタの定義
コンストラクタは、クラス内でconstructorという名前で定義します。
コンストラクタには、インスタンス生成時に渡された引数を受け取るための引数リストを指定します。
このRectangleクラスでは、constructorが定義されています。
constructorは、widthとheightの2つの引数を受け取ります。
thisキーワードを使って、インスタンスのプロパティwidthとheightに、それぞれ引数の値を代入しています。
また、getAreaメソッドも定義されており、インスタンスのwidthとheightを使って面積を計算して返します。
このようにコンストラクタを定義することで、インスタンス生成時に必要な初期化処理を行うことができます。
○サンプルコード4:newによるインスタンス化
newキーワードを使って、クラスからインスタンスを生成してみましょう。
new Rectangle(5, 3)のように、newキーワードの後にクラス名とコンストラクタの引数を指定することで、Rectangleクラスのインスタンスを生成しています。
生成されたインスタンスは、rectangle1変数に代入されます。
同様に、new Rectangle(10, 7)で別のインスタンスrectangle2を生成しています。
生成されたインスタンスのプロパティにアクセスすると、コンストラクタで初期化された値が取得できます。
rectangle1.widthは5、rectangle1.heightは3となっています。
また、rectangle1.getArea()を呼び出すと、面積が計算されて15が返ってきます。
rectangle2についても同様に、プロパティの値やgetArea()の結果が期待通りになっていることが確認できます。
このように、newキーワードとコンストラクタを使うことで、クラスからインスタンスを生成し、それぞれのインスタンスごとに個別のデータを持つことができます。
○サンプルコード5:コンストラクタへの引数
コンストラクタには、インスタンス生成時に必要な引数を渡すことができます。
これにより、インスタンスごとに異なる初期値を設定することが可能になります。
このCircleクラスでは、コンストラクタがradius引数を受け取ります。
この引数の値は、インスタンスのradiusプロパティに代入されます。
new Circle(5)とnew Circle(10)で、それぞれ異なる半径のCircleインスタンスを生成しています。
生成されたインスタンスのradiusプロパティには、コンストラクタに渡された引数の値が設定されています。
また、getArea()メソッドを呼び出すと、インスタンスのradiusを使って円の面積が計算されます。
●インスタンス変数とメソッド
クラスからインスタンスを生成すると、各インスタンスは独自のデータを持つことができます。
このデータを保持するための変数をインスタンス変数と呼びます。
また、インスタンスが持つ関数のことをインスタンスメソッドと呼びます。
インスタンス変数とメソッドを使うことで、オブジェクト指向プログラミングの利点であるデータと操作の組み合わせを実現できます。
インスタンス変数は、constructor内でthisキーワードを使って定義します。
thisはインスタンス自身を参照するキーワードで、this.変数名のように使うことで、インスタンスに属する変数を定義できます。
こうして定義されたインスタンス変数は、インスタンスごとに異なる値を持つことができます。
一方、インスタンスメソッドは、クラス内で定義された関数です。
このメソッドは、インスタンスに対して呼び出すことができ、インスタンス変数を操作したり、インスタンスに関連する処理を行ったりします。
それでは、サンプルコードを見ながら、インスタンス変数とメソッドの使い方を詳しく見ていきましょう。
○サンプルコード6:インスタンス変数の定義と使用
このPersonクラスでは、constructor内でthis.nameとthis.ageというインスタンス変数を定義しています。
これらの変数は、それぞれnameとageの引数で初期化されます。
introduceメソッドは、インスタンス変数nameとageを使って自己紹介のメッセージを出力します。
person1とperson2は、それぞれ異なるnameとageの値で初期化されたPersonクラスのインスタンスです。
person1.introduce()とperson2.introduce()を呼び出すと、それぞれのインスタンスのnameとageの値が使われて、自己紹介のメッセージが出力されます。
このように、インスタンス変数を使うことで、インスタンスごとに異なるデータを保持し、インスタンスメソッドからそのデータにアクセスすることができます。
○サンプルコード7:インスタンスメソッドの定義と呼び出し
インスタンスメソッドは、クラス内で定義された関数で、インスタンスに対して呼び出すことができます。
インスタンスメソッドは、インスタンス変数を操作したり、インスタンスに関連する処理を行ったりします。
このCalculatorクラスでは、constructor内でthis.resultというインスタンス変数を定義し、初期値を0に設定しています。
addメソッドは、引数numを受け取り、this.resultに加算します。
subtractメソッドは、引数numを受け取り、this.resultから減算します。
getResultメソッドは、現在のthis.resultの値を返します。
calcは、Calculatorクラスのインスタンスです。
calc.add(10)とcalc.subtract(3)を呼び出すことで、calcインスタンスのresult変数が操作されます。
calc.getResult()を呼び出すと、現在のresultの値が取得できます。
その後、calc.add(5)を呼び出すと、resultの値が更新され、calc.getResult()を呼び出すと新しい値が取得できます。
このように、インスタンスメソッドを使うことで、インスタンス変数を操作し、インスタンスに関連する処理を行うことができます。
○サンプルコード8:thisキーワード
thisキーワードは、インスタンス自身を参照するために使用されます。
thisを使うことで、インスタンス変数やインスタンスメソッドにアクセスすることができます。
このCounterクラスでは、constructor内でthis.countというインスタンス変数を定義し、初期値を0に設定しています。
incrementメソッドは、this.countの値を1増加させます。
getCountメソッドは、現在のthis.countの値を返します。resetメソッドは、this.countの値を0にリセットします。
counterは、Counterクラスのインスタンスです。
counter.increment()を2回呼び出すことで、counterインスタンスのcount変数が2増加します。
counter.getCount()を呼び出すと、現在のcountの値である2が取得できます。
counter.reset()を呼び出すと、countの値が0にリセットされ、counter.getCount()を呼び出すと0が取得できます。
●インスタンスの削除とメモリ管理
JavaScriptでインスタンスを扱う際、メモリ管理について理解することが重要です。
インスタンスが不要になった場合、適切に削除やメモリ解放を行わないと、メモリリークにつながる可能性があります。でも心配しないでください。
JavaScriptにはガベージコレクションという仕組みがあり、自動的にメモリを管理してくれます。
ガベージコレクションは、もう使用されなくなったオブジェクトを自動的に検出し、メモリから解放する仕組みです。
JavaScriptエンジンがこのガベージコレクションを行うため、開発者が明示的にメモリ解放を行う必要はありません。
ただし、ガベージコレクションの仕組みを理解しておくことで、メモリリークを防ぐことができます。
インスタンスのプロパティを削除する方法と、ガベージコレクションによるメモリ解放について、サンプルコードを見ながら詳しく説明していきますので、一緒に理解を深めていきましょう。
○サンプルコード9:インスタンスのプロパティ削除
インスタンスのプロパティを削除するには、delete演算子を使用します。
delete演算子は、オブジェクトのプロパティを削除し、メモリから解放します。
このStudentクラスは、name、age、gradeの3つのプロパティを持っています。
const student = new Student("太郎", 16, "高校1年生");で、Studentクラスのインスタンスstudentを生成しています。
最初のconsole.log(student.grade);では、studentインスタンスのgradeプロパティにアクセスし、"高校1年生"が出力されます。
delete student.grade;で、studentインスタンスのgradeプロパティを削除しています。
削除後のconsole.log(student.grade);では、gradeプロパティが存在しないため、undefinedが出力されます。
このように、delete演算子を使うことで、インスタンスのプロパティを削除し、メモリから解放することができます。
ただし、delete演算子はプロパティを削除するだけで、インスタンス自体は削除されません。
○サンプルコード10:参照の解放とガベージコレクション
JavaScriptでは、オブジェクトへの参照がなくなると、そのオブジェクトはガベージコレクションの対象になります。
ガベージコレクションは、定期的に不要になったオブジェクトを自動的に検出し、メモリから解放します。
このPointクラスは、xとyの2つのプロパティを持っています。
let point1 = new Point(1, 2);とlet point2 = new Point(3, 4);で、それぞれPointクラスのインスタンスpoint1とpoint2を生成しています。
point1 = null;とpoint2 = null;で、point1とpoint2の参照をnullに設定しています。
これにより、Pointインスタンスへの参照がなくなります。
参照がなくなったインスタンスは、ガベージコレクションの対象となります。
JavaScriptエンジンが定期的にガベージコレクションを実行し、これらのインスタンスを自動的にメモリから解放します。
ただし、ガベージコレクションのタイミングはJavaScriptエンジンに依存するため、開発者が明示的に制御することはできません。
参照を適切に管理し、不要になったインスタンスへの参照を解放することで、メモリリークを防ぐことができます。
●JavaScriptのインスタンス化実践例
これまでJavaScriptのクラスとインスタンスについて学んできましたが、実際のアプリケーション開発ではどのように活用されているのでしょうか?
ここからは、具体的な実践例を見ながら、インスタンス化の使い方を深めていきましょう。
サンプルコードを通して、ユーザー情報の管理、複数インスタンスの扱い方、インスタンスの状態の変更と取得など、よくある場面でのインスタンス化の活用方法を解説していきます。
一緒に手を動かしながら、インスタンス化の実践的なスキルを身につけていきましょう。
○サンプルコード11:ユーザークラスの作成
ウェブアプリケーションでは、ユーザー情報を管理するためにクラスを使うことがよくあります。
ここでは、Userクラスを作成し、ユーザーのプロパティとメソッドを定義してみましょう。
このUserクラスは、id、name、emailの3つのプロパティを持ち、それぞれのゲッターとセッターメソッドが定義されています。
const user = new User(1, "太郎", "taro@example.com");で、Userクラスのインスタンスuserを生成し、初期値を設定しています。
user.getId()、user.getName()、user.getEmail()を呼び出すことで、それぞれのプロパティの値を取得できます。
user.setName("花子")とuser.setEmail("hanako@example.com")で、userインスタンスのnameとemailプロパティの値を変更しています。
変更後のuser.getName()とuser.getEmail()を呼び出すと、新しい値が取得できることが確認できます。
このように、クラスを使ってユーザー情報を管理することで、データの構造化とカプセル化が実現できます。
○サンプルコード12:複数インスタンスの管理
アプリケーションでは、複数のユーザーを扱うことが一般的です。
ここでは、Userクラスを使って、複数のユーザーインスタンスを管理する方法を見てみましょう。
usersは、Userインスタンスを格納する配列です。
users.push()を使って、Userインスタンスを配列に追加しています。
それぞれのインスタンスは、異なるIDと初期値を持っています。
for...ofループを使って、users配列内の各Userインスタンスに対して処理を行っています。
ループ内では、各インスタンスのゲッターメソッドを呼び出して、プロパティの値を出力しています。
出力結果から、それぞれのUserインスタンスが個別のデータを持っていることがわかります。
このように、配列を使って複数のインスタンスを管理することで、データの一括処理や検索が容易になります。
○サンプルコード13:インスタンスの状態の変更と取得
インスタンスは、独自の状態を持ち、その状態を変更したり取得したりすることができます。
ここでは、Userクラスに新しいプロパティとメソッドを追加して、インスタンスの状態の変更と取得を行ってみましょう。
Userクラスに、isActiveプロパティを追加しています。
このプロパティは、ユーザーがアクティブかどうかを表すブール値です。初期値はtrueに設定されています。
activate()メソッドは、isActiveをtrueに設定し、ユーザーをアクティブ状態にします。
deactivate()メソッドは、isActiveをfalseに設定し、ユーザーを非アクティブ状態にします。
isUserActive()メソッドは、現在のisActiveの値を返します。
userインスタンスを生成した直後は、isActiveはtrueなので、user.isUserActive()はtrueを返します。
user.deactivate()を呼び出すと、isActiveがfalseに変更され、user.isUserActive()はfalseを返します。
user.activate()を呼び出すと、再びisActiveがtrueに変更され、user.isUserActive()はtrueを返します。
このように、インスタンスの状態を変更し、取得することで、オブジェクトの振る舞いを柔軟に制御できます。
まとめ
JavaScriptにおけるクラスとインスタンスについて、その概念から実践的な使い方まで解説してきました。
クラスは設計図であり、インスタンスはその設計図から生成される実体です。
newキーワードとコンストラクタを使ってインスタンスを生成し、インスタンス変数やメソッドを通してデータと操作を組み合わせることで、柔軟で効率的なプログラミングが可能になります。
それでは、本記事で解説した知識を活かして、より効率的で保守性の高いコードを書いていきましょう。


