【JavaScript】クラス定義(コンストラクタ、継承)はどうする?

JavaScriptでのクラス定義について書いていきたいと思います。参考になれば幸いです。
JavaScriptにはオブジェクト指向の考え方があります。クラス定義、コンストラクタや継承などはどういう記述になるのでしょうか?見て行きましょう。
JavaScriptでのクラス定義
JavaScriptでクラスを定義する方法は単純、functionを使います。functionと言えばJavaScriptでいう関数定義で使う記述ですが、なんとクラス定義にも使ってしまうのです。以下のように定義します。
この定義では、「Person(人間)クラス」という大きな枠(クラス)を作成し、その枠を持った「Tanaka」と言う個体(インスタンス)を定義しています。
「Tanaka」は「田中(name)、男(sex)」という情報(プロパティ)を持ち、”hello”と話す能力(メソッド)を持っています。
行単位で見て行きましょう。
1 2 3 4 |
var Person = function(name,sex){ this.name = name; this.sex = sex; }; |
クラスの定義をしています。ご覧の通り、functionを使っていますね。
「this.〜」という変数がありますが、これはプロパティ(インスタンス特有の変数)の定義です。
1 2 3 |
Person.prototype.hello = function(){ console.log('hello'); }; |
クラスのメソッドを定義しています。Person(人間)というクラスに”hello”と話させるメソッド(能力)です。
「Person.prototype」が「これからPersonクラスのメソッドを定義しますよ」という部分で、「hello」がメソッドの名前です。
- メソッドの定義にはprototype以外もある!?
2018年現在、よく見かけるのは上記の「prototype」です。しかし他の言語を試したことがある方なら、プロパティの定義と同様に「this」を使ってメソッドを定義してもいいじゃないか、と思うかもしれません。しかし両者には大きな違いがあるのです・・・。興味があれば以下の記事をどうぞ。
1 |
Tanaka = new Person('田中','男'); |
12行目で「Tanaka」というPersonクラスのインスタンスを定義しています。
その際に、引数として”田中”、”男”という文字列を与えていますね、これは3〜4行目の記述に繋がっていて、Tanakaはnameが”田中”、sexは”男”という初期情報を定義しています(コンストラクタ)。
これでJavaScriptでクラスが定義できるようになりました。
JavaScriptでの継承
さて、JavaScriptでクラスの定義ができるようになりましたが、オブジェクト指向にはまだまだ重要な考え方があります。
それは親から子への引き継ぎ、つまり継承(オーバーライド)です。
もちろん、JavaScriptでも継承はできます。どのような記述になるのでしょうか?以下のソースを見てください。
今回は新たに「Student」というクラスを作成しました。前章の「クラス定義」で定義した「Person」(人間)でできること全てを「Student」(学生)でもできるように継承しています。
行単位で見ていきましょう。
1 2 3 4 |
function Student(name,sex,classnum){ Person.call(this,name,sex) //Personのコンストラクタを使用(コンストラクタ継承) this.classnum = classnum; //Student独自のプロパティ } |
Studentクラスを定義しています。ここで注目していただきたいのが、3行目のPerson.callの記述です。Personのコンストラクタ(nameとsexの定義)を継承しているのですが・・・いきなりcallなんて使われてもわかりませんよね。ということで簡単にcallの解説をしていきます。
function.callメソッドとは?
以下のような書き方をします。(この例ではfunctionを関数と捉えた方がわかりやすいです。)
“[使用したいfunction].call( [funtionの処理だけ利用したいオブジェクト] , [使用したいfunctionの引数内容] )”
要するに、「俺、この特技(使用したいfunction)持ってないんだけどさ、わざわざ身につける(定義)するのも面倒だしお前の特技を使わせてよ。結果だけもらうからさ。」
というわがまま理論を通すことができる機能になっています。とても便利ですね。
上記の解説を踏まえた上で、Person.call(this,name,sex)と言う記述の各要素はそれぞれ何に当たるのかというと・・・
- 使用したいfunction = Personというfunction。これは関数的にいうとthis.nameとthis.sexを定義する機能だけを持っています。(コンストラクタ)
- functionの処理だけ利用したいオブジェクト = this(Student)のことです。
- 使用したいfunctionの引数内容 = name,sexのことです。Personの引数はこの2つでしたね。
結果として、Studentというクラスはnameとsexの定義をPersonクラスの機能を使って行なっているわけです。
これでコンストラクタの継承はできました。続いてはメソッドの継承です。
1 2 |
Student.prototype = Object.create(Person.prototype); Student.prototype.constractor = Student; |
Object.createというメソッドが出てきましたね。要するに、「StudentはPersonのprototypeメソッドを持つんだぞ」ということを宣言しています。
問題はその下の記述です。constractorというメソッドにわざわざ自分を定義しています。どうしてでしょう?
実は、functionは定義された時点で「prototype.constractor」というメソッドが自動で定義されていて、この値は定義された時点でのfunction自身が指定されます。
Person.prototype.constractorを使えばPersonを返しますし、Student.prototype.constractorを使えばStudentを返すべきなのです。
しかし、先ほど「StudentはPersonのprototypeメソッドを持つんだぞ」という宣言をしてしまいました。
この状態でStudent.prototype.constractorが呼ばれるとどうなってしまうのか・・・・Personと返ってきてしまいます。ここはStudentと返ってきて欲しいですね。
だから、上記のような記述をして「StudentはPersonのprototypeメソッドを持つんだぞ。だけどprototype.constractorはStudentだぞ。」という宣言をしています。
これでメソッドの継承は完了です。実際に継承してみましょう!
1 |
var Suzuki = new Student('鈴木','男',3); |
これでSuzukiと言うStudentクラスのインスタンスが定義されました!Personと比べると「3」というプロパティが増えていますね!
「クラス定義(コンストラクタ、継承)はどうする?」についてのノウハウは以上です。
よろしければ他のノウハウもみていってください。