コンストラクタ関数 [JavaScript]
もりけん塾(@terrace_tech)にて、JavaScriptを勉強しています。
今回は、コンストラクタ関数について調べていきたいと思います。
コンストラクタ関数の仕組みを理解する必要がある理由
コンストラクタ関数について学んでいくと、newキーワードやthis、プロトタイプなど様々な用語が出てきます。これらはすべて、コンストラクタ関数でインスタンスを作成する際に知っておくべきコンセプトです。
オブジェクトを作成する方法に関しては今後、classやファクトリ関数なども詳しく見ていく予定ですが、コンストラクタ関数の仕組みを理解することによって、別の方法と比較する際に、有利な点や不利な点を把握しておけると思いました。
コンストラクタ関数とは
コンストラクタ関数は、オブジェクトを作るひな型の関数です。
オブジェクトを作成するためには色々な方法がありますが、コンストラクタ関数はそのうちの一つです。
newキーワードを使用して、新しいオブジェクトを作成することが出来ます。
コンストラクタ関数はnewキーワードを使用して呼び出すと、通常の関数とは違った動きをします。
コンストラクタ関数以外でオブジェクトを作る場合
先ほど「オブジェクトを作成するためには色々な方法がある」と書きましたが、オブジェクトを作成する方法の一つとして、オブジェクトリテラルという書き方があります。
オブジェクトリテラルでオブジェクトを作る場合は、1個1個、オブジェクトのプロパティ(下記の例の場合、title / author / yearの部分)を書いていきます。
// 毎回すべてのプロパティを書く
const book1 = {
title: "人間失格",
author: "太宰治",
year: 1948
}
const book2 = {
title: "吾輩は猫である",
author: "夏目漱石",
year: 1905
}
const book3 = {
title: "羅生門",
author: "芥川龍之介",
year: 1915
}
コンストラクタ関数を使用してオブジェクトを作る場合
コンストラクタ関数を設定する
コンストラクタ関数を設定することにより、あらかじめオブジェクトが持つプロパティを設定することが出来ます。なのでオブジェクトを作るたびに毎回同じプロパティを書く必要がなくなります。
コンストラクタ関数を作成する場合、関数名の最初の文字は通常、大文字にします。例えばtitle、author、yearというプロパティを持たせたい場合は、下記のように設定します。
// コンストラクタ関数
function Book(title, author, year) {
this.title = title;
this.author = author;
this.year = year;
}
オブジェクトを作る
コンストラクタ関数を元にして作られたオブジェクトを、インスタンスと呼びます。
コンストラクタ関数でインスタンスを作成するには、newというキーワードを使い、関数を呼び出します。
// newキーワードを使用したインスタンスの作り方
const book1 = new Book("人間失格", "太宰治", 1948);
const book2 = new Book("吾輩は猫である", "夏目漱石", 1905);
const book3 = new Book("羅生門", "芥川龍之介", 1915);
newを付けてコンストラクタ関数を呼び出すと何が起こるのか
コンストラクタ関数自体は実は特別なものではなく、通常の関数と同じただの関数です。しかしnewキーワードを付けて呼び出されると、通常の関数とは違った動きをします。
例として、下記のようなコンストラクタ関数とそのプロトタイプを用意し、newキーワードがどんな動きをしているかを詳しく見ていきます。


newキーワードを付けてコンストラクタ関数を呼び出すと、下記の4つのことが自動で行われます。

1.
まず、プロパティが何も設定されていない状態の空のオブジェクトが作成されます。
(※あくまでプロセスであって、実際にconsole.logをしても空のオブジェクトが作られる瞬間が表示されたりはしません。)
2.
1で作成されたオブジェクトの_proto_ の中に、prototypeが設定されます。
例えば、下記のようなprototypeがコンストラクタ関数に設定されていたとすると、newキーワードで作られたインスタンスの _proto_ には、printTitle が設定されます。

3.
コンストラクタ関数の中でthis を使って書かれたプロパティがすべて、オブジェクトにセットされます。

ただし、もしもコンストラクタ関数の中に this が付いていないものがあれば、それに関してはオブジェクトの中に含まれません。
詳しく確認するために、コンストラクタ関数の中にvar local = 1;と追加してみます。

そしてnewキーワードを付けて、上記のコンストラクタ関数を呼び出します。

作られたインスタンスを見てみると、変数localは含まれていないことが分かります。

これは変数localは、コンストラクタ関数内のスコープでのみ有効だからです。
もし変数localの値にアクセスしたい場合は、同じ関数内から、変数localにアクセスするメソッドを作ります。

book.printLocal()を実行すると、変数localの値を表示させることが出来ました。

4.
新しいオブジェクトが返されます。
ただし、もしコンストラクタ関数内に return と設定されていると、オブジェクトを返してくれない場合があります。
3 で確認した通り、もしコンストラクタ関数がプリミティブ型の値をreturnしている場合は、その値は単純に無視され、オブジェクトが返されます。
ですが、もしプリミティブ型ではなくオブジェクトをreturnしている場合は、thisで設定した値は、新しく生成されたオブジェクトにはセットされません。
例えば、コンストラクタ関数の中に下記のようなオブジェクトをreturnする記述があったとします。

上記のコンストラクタ関数を呼び出してインスタンスを作ります。

作られたオブジェクトの詳細を見てみます。

this.title = titleなどと設定したプロパティはすべて消え、returnされたオブジェクトのみが設定されていることが分かります。
コンストラクタ関数内のthisは何を指しているか
thisは、コンストラクタ関数を使って生み出されたインスタンスを指しています。
実際にthisがどのように表示されるか、コンソールに打ち出しながら確認していきます。
まずコンストラクタ関数に、this.title が何を指しているかを表示出来るメソッドを持たせます。


上記のメソッドを作成した後、新しいインスタンスを作成します。

3つのオブジェクトでそれぞれprintTitle メソッドを呼び出し、this.titleを表示させてみます。



3つのオブジェクトのthisがそれぞれ、インスタンス自身を指していることが明確になったと思います。
まとめ
コンストラクタ関数について学んでいく際、newキーワードやthis、プロトタイプなど色々な用語が出てきました。それらを言葉で説明しようとしても上手く出てこず、これまで漠然としか把握していなかったことに気が付きました。
そういった項目を細かく見ていくことによって、これまで少ししか理解出来ていなかったコンストラクタ関数への理解が今回より深まったように思います。
参考にしたサイトなど
JavaScript new Keyword|TutorialTeachers
newキーワードにより呼び出されたコンストラクタ関数がどのような動きをするのか、分かりやすい図を交えて説明しています。
new 演算子|MDN Web Docs
オブジェクトの概要や扱い方が載っています。
JavaScriptを教えていただいている、もりた先生の詳細はこちらです。
[もりけん塾]
ブログ:http://kenjimorita.jp
Twitter:https://twitter.com/terrace_tech