JavaScriptはクラスをサポートしないため、ここで解説するのは正確な意味でのクラスとは異なります。
functionキーワードを用いて定義します。これは通常の関数を定義する構文と同一です。
// クラス (コンストラクタ) function Func() { // プロパティ this.x = 10; }
このように定義したクラスは、new演算子でインスタンス化できます。
var func = new Func();
クラスのprototypeプロパティに、関数リテラルの構文で定義します。ちなみにこのprototypeプロパティは、Objectクラスから継承されるものです。prototype - JavaScript | MDN
function Func() {} Func.prototype.A = function() {}
またはオブジェクトリテラルの構文を使用することで、複数のメソッドを続けて定義できます。
Func.prototype = { A : function() {}, B : function() {} };
ただしオブジェクトリテラルを使用できるのは、新規にメソッドを定義する場合のみです。たとえば、
function Func() {}
Func.prototype =
{
A : function() {},
B : function() {}
};
Func.prototype = // ここで上書きされる
{
C : function() {}
};
のようにprototypeプロパティに追加しようとすると、既存のプロパティに上書きされるために先の定義が失われてしまいます。これはオブジェクトのプロパティを定義することを考えれば、容易に理解できることです。
なおprototypeプロパティを省いて定義するとクラスメソッドとなり、まったく違う意味を持ちます。
prototypeプロパティを利用することで、NumberやStringといった組み込みオブジェクトにメソッドを追加できます。たとえばNumberにメソッドを追加するには、
Number.prototype.toHex = function() { return this.toString( 16 ); }
のようにします。メソッドを新規に作成するわけではないため、オブジェクトリテラルの構文は使用できません。
この方法は便利ではありますが、軽率に使用するとコードの保守性を低下させるため、不用意に使用すべきではありません。適した使い方は、標準に準拠しないJavaScriptの実装に、標準に準拠したメソッドを追加するような場合です。
プロパティのように、thisキーワードでメソッドを定義することでも可能です。
var Func = function() { this.A = function() { return 'A'; } // メソッド this.x = 1; // プロパティ var B = function() { return 'B'; } // ローカルスコープの関数 C = function() { return 'C'; } // グローバルスコープの関数 function D() { return 'D'; } // ローカルスコープの関数 } var func = new Func(); alert( func.A() ); // OK: 'A'が返される alert( func.x ); // OK: 1が返される alert( func.B() ); // TypeError: func.B is not a function alert( func.C() ); // TypeError: func.C is not a function alert( func.D() ); // TypeError: func.D is not a function alert( B() ); // ReferenceError: B is not defined alert( C() ); // OK: 'C'が返される alert( D() ); // ReferenceError: D is not defined
ただしこの方法では、クラスをインスタンス化するたびにメソッドのコピーも作成されるため、メモリが無駄に消費されます。
プロパティの定義方法はメソッドと同様ですが、関数以外のデータ型を用いるとそれがプロパティと見なされます。プロパティへのアクセスは、コンストラクタまたはメソッドからはthisを介して行います。一方でクラス外からは、クラスのインスタンスを介します。
function Func() { this.x = 1; } Func.prototype.A = function() { // メソッド内からのプロパティへのアクセス this.x++; } var func = new Func(); // インスタンスからのプロパティへのアクセス func.x++;
関数内でのthisの意味は、その関数の呼び出され方によって異なります。
またイベントハンドラ内でのthisの意味は実装によって異なりますが、基本的にイベントを発生したインスタンスへの参照となります。
以下のコードで示すように、メソッド内から関数として呼ばれた関数内では、thisはグローバルオブジェクトを参照します。そのためクラスのプロパティにはアクセスできません。
function Func() { this.x = 1; } Func.prototype.A = function() { // メソッドとして呼ばれているので、 // thisはfuncを参照する alert( this.x ); // 1と表示 // メソッド内に入れ子にされた関数 function B() { // 関数として呼ばれているので、 // thisはグローバルオブジェクトを参照する alert( this.x ); // undefinedと表示 } B(); // 関数の呼び出し } var func = new Func(); func.A(); // メソッドの呼び出し
var x = 1; // グローバル変数 var obj = { x:2, // オブジェクトのプロパティ A:function() // { alert( this.x ); } }; alert( x ); // 1と表示 (グローバル変数) alert( obj.x ); // 2と表示 (オブジェクトのプロパティ) obj.A(); // 2と表示 (Aはオブジェクトのスコープで呼ばれる) // オブジェクトが参照している関数をコピーする var B = obj.A; B(); // 1と表示 (Bはグローバルのスコープで呼ばれる)
以上のことから関数内でthisを使用する場合には、その関数の呼ばれ方に留意する必要があります。
プロパティに基本型の変数を指定した場合、そのクラスがインスタンス化された時点の変数の値で初期化されます。
var num = 1; // 基本型 (数値) var obj = { a: 1 }; // 参照型 (オブジェクト) function Func() { this.x = num; this.y = obj; } var func = new Func(); alert( func.x ); // 1 alert( func.y.a ); // 1 // プロパティの初期値としている変数の値を変更する num = 2; obj.a = 2; alert( func.x ); // 1 … 影響なし alert( func.y.a ); // 2
これを、基本型の変数でもその実体に直接アクセスできるようにするには、ゲッターやセッターを用います。
クラス定義を行った関数が、コンストラクタとなります。コンストラクタの戻り値は意味を持たないため、returnは記述しないようにします。
// クラス (コンストラクタ) function Func() { // returnを記述しない }
なおオブジェクトのコンストラクタは、constructorプロパティで参照できます。
var func = new Func();
alert( func.constructor == Func ); // true
引数を持つ関数のように定義することで、引数付きコンストラクタとなります。
function Func( a ) { this.x = a; }
JavaScriptでは、言語仕様としてのデストラクタのサポートはありません。
終了処理を行う必要があるならばそれを行うメソッドを定義し、そのメソッドを明示的に呼び出すようにします。
function Func() {} Func.prototype.Destructor = function() // デストラクタのつもり { // 終了処理 } var func = new Func(); ... func.Destructor(); // デストラクタの呼び出し
これらはインスタンスではなく、クラスそのものに関連付けられたメンバです。C++を例にするならば、static属性を付けた静的メンバに相当します。
定義方法は、オブジェクトのプロパティのそれと同一です。
コンストラクタのプロパティとして関数を設定すると、それがクラスメソッドとなります。またクラスメソッド内でのthisキーワードは、そのクラスのインスタンスではなくコンストラクタ関数を参照します。
function Func() { this.x = 1; // プロパティ } Func.x = 2; // クラスプロパティ // クラスメソッドの定義 Func.A = function() { return this.x; // クラスプロパティを参照するため、2が返される } // クラスメソッドの呼び出し Func.A();
function Func() {} Func.prototype.B = function() {} // メソッド Func.B = function() {} // クラスメソッド Func.prototype.A = function() { this.B(); // メソッドが呼び出される } Func.A = function() { this.B(); // クラスメソッドが呼び出される }
コンストラクタのプロパティとして変数を設定すると、それがクラスプロパティとなります。
function Func() { this.x = 1; // プロパティ } // クラスプロパティ Func.x = 2; var func = new Func(); var a = func.x; // プロパティの参照: 1が返される var b = Func.x; // クラスプロパティの参照: 2が返される
すべてのJavaScriptオブジェクトにはプロトタイプ オブジェクトというオブジェクトへの参照が含まれ、すべてのオブジェクトはこのプロトタイプからプロパティを継承しています。
// スーパークラス
function SuperClass( a, b )
{
this.a = a;
this.b = b;
}
SuperClass.prototype.Func1 = function() {}
// サブクラス function SubClass( a, b, c ) { // スーパークラスのコンストラクタを呼び出し、スーパークラスのプロパティを初期化する SubClass.call( this, a, b ); // 新しいプロパティを追加する this.c = c; } // スーパークラスのサブクラスとなるように、プロトタイプオブジェクトを生成する SubClass.prototype = new SuperClass(); // サブクラスのコンストラクタが参照されるように、既定のconstructorプロパティを書き換える SubClass.prototype.constructor = SubClass; // 新しいメソッドを追加する SubClass.prototype.Func2 = function() {} // 継承したくないプロパティを、サブクラス側で削除する delete SubClass.prototype.a;