●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
キーワードとコンストラクタを使ってインスタンスを生成し、インスタンス変数やメソッドを通してデータと操作を組み合わせることで、柔軟で効率的なプログラミングが可能になります。
それでは、本記事で解説した知識を活かして、より効率的で保守性の高いコードを書いていきましょう。