JS におけるクラス定義とは
JS において、クラスは関数によって定義します。つまり、クラスと言っていますがそれは便宜的な表現であって、実際には関数オブジェクトという名のオブジェクトです。これが、オブジェクトのコンストラクタとなります:
function MyClass() { this.property = '...'; this.method = function (){ ... }; } var myObj = new MyClass;
例において、myObj は MyClass のインスタンスとなり、クラスとインスタンスという関係が成り立つわけです。
この時に問題となることがあります。それは、インスタンスが生成される度に、クラス定義の中身が新たにメモリー上にロードされてしまう、ということです。複数のインスタンスを同時に持ちたい時に無駄にメモリーを消費してしまうことになります。
そこで登場するのが prototype というプロパティーです (JS = プロトタイプ・ベースのオブジェクト指向言語、ということの意味が象徴的に示されています)。クラスのメソッド定義を prototype を介して行うことで、各インスタンスはそのクラスの prototype の内容を参照するようになります:
function MyClass() { } MyClass.prototype.method = function (){ alert('Hi!'); }; var myObj = new MyClass; MyClass.prototype.method = function (){ alert('Ho!'); }; var myObj2 = new MyClass; myObj.method(); // => Ho! myObj2.method(); // => Ho!
なお、インスタンス変数はインスタンス毎に独立したものとなります。
実は、prototype.js の提供するクラス定義の仕組みとは、強制的に prototype プロパティーを通じてクラス定義を行わせる、というものに他なりません。
prototype.js の場合、空の関数の中に initialize という (Ruby と同じ) 初期化メソッドを呼び出す一文が組み込まれていることがポイントです。これにより、インスタンスの初期化を (コンストラクタへの引数があれば、それに応じて) 行うことができるわけです:
function MyClass() { /* * コンストラクタ (関数) への引数は arguments と言う、配列に似たオブジェクト * に格納される。そのままでは不都合なので、apply というメソッドを使うこと * によって個々の項目に展開し、初期化メソッドに渡す。 * 第一引数は、このインスタンスにおいてメソッドを実行せよ、という指示 */ this.initialize.apply(this, arguments); } MyClass.prototype = { initialize: function (arg1, arg2, arg3) { // インスタンス変数は通常ここで宣言・定義する } }
基本はこんなところですが、さらに以下のことも押さえておくと便利でしょう。
クラス・メソッド
個々のインスタンスとは直接関わりの無い、あるいはインスタンス全体を管理するような機能を実装したい時に使います。
MyClass.method = function (){ ... }; MyClass.method(); // クラスをレシーバーとして呼び出す // Cf: MyClass.prototype.method = function (){ ... }; // インスタンス・メソッド (new MyClass).method(); // インスタンスをレシーバとする
クラス変数及び定数
クラス・メソッドと同様です。JS には定数はありませんが、大文字によって変数と区別し、値を変えないと心に決めて運用すれば良いのです。
MyClass.variable = 'variable value'; MyClass.CONSTANT = 'constant value';
クラスのグルーピング
関連するクラスの一群を一つのネームスペースにまとめることができます。クラス・メソッド等と区別するために、大文字からクラス名を始めるようにすると良いでしょう。
function MyClass.SubClass() { MyClass.SubClass.prototype.method = function (){ ... }; } // 記法 2 (prototype.js 式) MyClass.SubClass = function (){}; // 無名関数を使う MyClass.SubClass.prototype = { ... };