概要
- Javascriptにはクラスという概念はない
- コンストラクタ定義+newによってインスタンスを生成する。
- ES6以前ではclass構文が実装がされていなかったので、prototypeという機能を使ってclass構文を実装していた
コンストラクタについて
1.コンストラクタ定義を行い(作業①)、newを付ける事(作業②)の2つの作業で、コンストラクタ定義によるインスタンスを生成する。newをしない場合は、単なる関数の実行となる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
//コンストラクタ定義 function Func(name, value) { this.name = name; this.print = function() { console.log(value); } } //newした場合 var f1 = new Func("aaa", "bbb"); console.log(f1.name); //aaaが表示 f1.print(); //bbbが表示 //グローバル変数nameを定義 name="zzz"; //上でnewしているので、Func内のname変数はグローバル変数でない console.log(f1.name); //aaaが表示 //グローバルな関数を定義 function print(){ console.log("xxx"); } print(); //xxxが表示される //newしない場合 var f2 = Func("111", "222"); //関数実行となり、func内のthisはグローバルオブジェクトを示し、name, printはグルーバルとして定義される。 console.log(f2.name); //関数の戻り値はないので、コンパイルエラー f2.print(); //関数の戻り値はないので、コンパイルエラー //newしない場合のFunc("111", "222");の関数実行によって、グローバルとして定義さえたnameとprintが上書きされる console.log(name); //111が表示(name変数が上書きされたため、zzzでは表示されない) print(); //222が表示(print関数が上書きされたため、xxxでは表示されない) |
(newをした場合)
- インスタンスを生成する
- Func内のthisは、コンストラクタ呼出しパターンのthisを示し、Func1内で生成されたFunc1のインスタンス(後から説明する暗黙で生成されたthis)を示す。
- newを付けた場合、内部的に以下の処理が行われている事と等価になる
1 2 3 4 5 6 7 8 9 |
//コンストラクタ定義 function Func(name, value) { //var this = {}; ← 暗黙的に実行(インスタンスの生成) this.name = name; this.print = function() { console.log(value); } // return this; ← 暗黙的に実行 } |
newを付けた場合は、以下の通りの処理を暗黙的に実行。
(1)毎回新しい空オブジェクトを生成する(var this = {})
(2)そのオブジェトに初期プロパティを加える。
(3)そのオブジェクトを「return」する。
(newしなかった場合)
- Func("111", "222");は単に関数の実行となる。
- 戻り値のない関数となるため、f2はundefiledとなる。
- Func内のthisはグローバルオブジェクトと解釈され、nameとprintはグローバルとして扱われる。そのため、Func("111", "222");実行前に定義されたname変数とprint関数を上書きする。
2.コンストラクタ定義をせず(newを付けずに)、インスタンス化する方法
- Funcは関数として動作させて、関数内で生成したインスタンスを戻り値として戻す。
- 例え、new付けても同じ動きとなる。
(例1)明示的にローカル変数「self」を「return」してるので、thisの暗黙のルールは適用されない。
1 2 3 4 5 6 7 8 |
function Func(name, value) { var self = {}; //インスタンスの生成 self.name = name; self.print = function() { console.log(value); }; return self; } |
(例2){・・・}インスタンス化して、そのまま返す。
1 2 3 4 5 6 7 8 |
function Dog(name, value) { return { name: name, print: function() { console.log(value); } } } |
prototypeプロパティ
- 新規に関数を生成するとprototypeプロパティが自動で作成される
- prototypeプロパティは自分の親への参照を行う方法
1 2 |
//関数を生成した時点で、Func1.prototypeプロパティが自動で生成されます。 function Func1() {} |
(プロトタイプ例)
1 2 3 4 5 6 7 8 9 10 |
//関数を生成した時点で、Func1.prototypeプロパティが自動で生成されます。 function Func(param) { this.value = param } Func.prototype.print = function() { console.log(this.value); }; var f1 = new Func('TEST'); f1.print(); //'TEST'; |
(類似する記述方法)
1 2 3 4 5 6 7 8 9 10 |
//prototypeと類似する記述方法 function Func(param) { this.value = param; this.print = function() { console.log(param); }; } var f1 = new Func('TEST'); f1.print(); //'TEST'; |
- 上記よりプロトタイプの記述方法が良い。
- 上記だと、newの実行で暗黙の var this = {}が生成されて、毎回毎回、関数の生成が実行されて効率が悪い。
クロージャ
- JavaScriptは関数ごとにスコープが作られる。
- クロージャとは「自分を囲むスコープにある変数を参照できる関数」の事で、内側の関数が外側の関数の変数を参照できる機能。
- private変数(インスタンス変数)のように扱える。インスタンスにインスタンス変数を持たせる(保持させる)ことができ、その変数をメソッド処理で参照可能とする。
1 2 3 4 5 6 7 8 9 10 11 12 |
function Func() { this.value = 0; this.innerFunc = function() { value ++; console.log(value); } } var f = new Func(); f.innerFunc(); // 1 f.innerFunc(); // 2 f.innerFunc(); // 3 |
内側の関数innerFuncが外側の関数Funcの変数を参照できている。
参考にさせて頂いたページ
・JavaScriptのプロトタイプからオブジェクト指向を学ぶ
・JavaScriptでクロージャ入門。関数はすべてクロージャ?