Javascriptのオブジェクトの基本:インスタンスとコンストラクタ、代入式
Javaには「インスタンス」、「コンストラクタ」という言葉がある。
自分はJavaEE屋さん歴が長いので、Javascriptを書いていても、new演算子が出てくると『インスタンスを生成する』と言いたくなる。
だが、Javascriptでは『クラス』という言葉が使われないので、javaでいうところの、
クラス ==(活性化)==> インスタンス
という言い方はしっくりしない。
Javaでいう『クラス』は、
(一般的な意味での)オブジェクトの(Javaにおける)設計図
という意味を持つ。これと照らした場合、Javascriptでは『オブジェクト』と言ってしまうのが一般的だろう。
とすれば、(言葉遊びの感が否めないが)上の図式は、
オブジェクト ==(活性化)==> インスタンス
となる。
また、Javascriptの文脈で『コンストラクタ』という言葉に遭遇することがある。これは、
(活性化する)関数オブジェクト内の「スクリプトの実行」
を意味している。
スクリプト内で、よく「this」とかかれるが、これは「活性化したオブジェクト(インスタンス)そのもの」を表す。(この辺りの記述は曖昧なので、別途、考察したい)
このログでは、(関数オブジェクトに関連する演算の)基本的な性質と、コンストラクタの動きを追ってみたい。
以下に簡単な例を示す。
<HTML> <HEAD> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <META http-equiv="Content-Style-Type" content="text/css"> <TITLE>Object Sample</TITLE> <style type="text/css"> </style> <script type="text/javascript" src="scripts/lib/prettyprint.js" > </script> <script type="text/javascript">//<![CDATA[ /* * オブジェクト操作のサンプル */ // インスタンス化する関数 var test = function(){ // --- (8) // コンストラクタ: thisは、活性化されたオブジェクト自身をさす。 this._arr = ["pretty","print"]; var _arr1 = ["permanent","vacation"]; this.getArr1 = function(){return _arr1;} }; var obj1 = test; // --- (1) var obj2 = new test(); // --- (2) testを活性化させて(インスタンス化して)代入 var obj3 = obj2; // obj3にobj2を代入してみる。--- (7) // プロパティーへのアクセスを試みる。 test._arr = ["hello","hoge"]; // ---(3) これでobj1が変わる。 obj2._arr = ["hello","hogehoge"]; // --- (4) これでobj3が変わる。(7)が参照の代入だとわかる。 // PrettyPrintでダンプ function init() { // _arrのテスト --- (5) alert('obj1._arr: '+obj1._arr+ ', obj3._arr: '+obj3._arr); // _arr1のテスト --- (6) alert('obj3._arr1: '+obj3.getArr1()); document.getElementById('debug1').appendChild(prettyPrint(obj1)); document.getElementById('debug2').appendChild(prettyPrint(obj3)); } // ]]> </script> </HEAD> <BODY onload="init()"> <!-- prettyPrint.jsレンダリング用 --> <div id="debug1" style="width:450px;"></div> <div id="debug2" style="width:450px;"></div> </BODY> </HTML>
実験していることは簡単で、
// インスタンス化する関数 var test = function(){ // コンストラクタ: thisは、活性化されたオブジェクト(インスタンス)自身をさす。 this._arr = ["pretty","print"]; var _arr1 = ["permanent","vacation"]; // --- (a) this.getArr1 = function(){return _arr1;} // --- (b) };
という関数(オブジェクト)をページスコープに定義する。
そして、まず、(1)でobj1にtestを代入しておく。先日のログ『Javascriptの基本:代入式』で検証したように、obj1には「test、すなわち、(8)式の右辺の関数への参照」が代入される。
また、(2)は、obj2にtestのインスタンスを代入する典型的な構文である。
この代入式も、
(左辺の)インスタンスの参照を(右辺に)セットする
のだが、それがはっきり分かるように、obj3を設けてobj2を代入しておく。(7)
次に(3)で、test._arrを変更してみる。当然、obj1._arrは変化するが、obj2._arrは影響をうけない(インスタンス化によるカプセリング)。
obj2._arrを変更するためには、(4)のように直接obj2インスタンスを操作する。これは、先のログ『Javascriptのオブジェクトの基本:オブジェクトの動的な変更』でふれた「(オブジェクトの)動的変更」である。
これらを実行してみると、(5)の結果は以下のように表示され、以上のことを裏付ける。
それでは、ローカル変数の_arr1((a)式)は実行されているのだろうか?(b)はこれを調べるためのアクセッサである。
(6)式で以下の画面が戻ってくるので、実行されていることがわかる(この検証方法では、アクセッサ評価時に(a)が実行されているかもしれない、という可能性を否定できないが、インタープリタであることから実行されていると推測)。
以下は、ためしに、obj1とobj3をprittyPrintでダンプしたものである。
obj3からは、obj2が見えており、オブジェクトの代入式も参照を渡すものであることがはっきりわかる。
また、obj3内のローカル変数(_arr1)は表示されないようである(これは問題かも)。