オブジェクトとはその名の通り「物」のことです。現実世界には色々な物があります。例えば、はさみ、鉛筆、本などは「物」です。実際にはプログラムでは「状態」を変数で表し、「ふるまい」を関数(メソッド)で表すことになります。
そういった物には「状態」や「ふるまい」があります。例えば、鉛筆であれば、「状態」は線の色、色の濃さ、線の太さなどで、「ふるまい」は線が引けるなどです。
こういった物の「状態」や「ふるまい」をプログラムに持ち込んだものがオブジェクト指向です。
var date = new Date();の date は Date クラスのインスタンスということになります。
function Rectangle( w, h ){
this.width = w ; // 長方形の幅
this.height = h ; // 長方形の高さ
}
これで Rectangle クラスのコンストラクタ関数は定義できました。var r0 = new Rectangle( 2, 1 ); var r1 = new Rectangle( 4, 2 );これで r0 と r1 は Rectangle クラスのインスタンス(オブジェクト)になりました。
width,height を取り出すには次のようにします。
status = r0.width ; // 結果は 2ドット(.)演算子 を使用して、その後ろにプロパティ名を指定することになります。簡単ですね。
// コンストラクタ関数の定義
function Rectangle( w, h ){
this.width = w ; // 長方形の幅
this.height = h ; // 長方形の高さ
}
// この関数がメソッドになる。
// 内部で this キーワードを使用していることに注意
function getArea_Rectangle(){
return this.width * this.height ;
}
// インスタンス生成
var r0 = new Rectangle( 3, 2 );
// メソッドの定義
r0.getArea = getArea_Rectangle ;
// メソッドの呼び出し
status = r0.getArea(); // 結果は 3*2 で 6
このようにメソッドを追加するときはドット(.)演算子の後ろでメソッド名を指定して = のあとに実際に処理する関数を指定します。簡単ですよね。
// コンストラクタ関数の定義
function Rectangle( w, h ){
this.width = w ; // 長方形の幅
this.height = h ; // 長方形の高さ
}
// この関数がメソッドになる。
// 内部で this キーワードを使用していることに注意
function getArea_Rectangle(){
return this.width * this.height ;
}
// メソッドの定義
Rectangle.prototype.getArea = getArea_Rectangle ;
// インスタンス生成
var r0 = new Rectangle( 3, 2 );
var r1 = new Rectangle( 4, 6 );
// メソッドの呼び出し
status = r0.getArea(); // 結果は 3*2 で 6
status = r1.getArea(); // 結果は 4*6 で 24
prototype で追加されたメソッドは全インスタンスで共有(に継承)されます。var r0 = new Rectangle( 3, 2 ); var r1 = new Rectangle( 4, 6 ); status = ( r0 == r1 );これは、予想通り false が表示されます。では、次はどうでしょう。
var r0 = new Rectangle( 1, 1 ); var r1 = new Rectangle( 1, 1 ); status = ( r0 == r1 );true が返されると思いますか?
var r0 = new Rectangle( 1, 1 ); var r1 = r0 ; status = ( r0 == r1 );これは true が表示されます(これは参照している場所が一緒のためにこうなります)。
var r0 = new Rectangle( 4, 3 ); var r1 = r0 ; r0.width = 5 ; status = r1.width ;答えは 4 ではなく、5 です。
var r0 = new Rectangle( 4, 3 ); var r1 = new Rectangle( r0.width, r0.height ); r0.width = 5 ; status = r1.width ;こうしておけば、ステータスバーに表示されるのは 4 です。
number,boolean,string)と参照型(object,array,function)の大きな違いですが、分かりましたでしょうか?
クラス:物の状態やふるまいを定義した箱(Rectangleなど)
インスタンス:クラスから生成(new)された実際のもの(r0,r1など)
インスタンス変数:インスタンス固有で持っている値(width,height など)
インスタンスメソッド:クラスのインスタンス全てで共有する関数(getArea など)
クラス変数:クラスそのものに関連付けられた変数(Math.PI など)
クラスメソッド:クラスそのものに関連付けられた関数(String.fromCharCode など)
メンバ:クラスの内部にある変数などの構成要素全体
Rectangle.SQRT2 = 1.414 ;終わりです(笑)
function Rectangle( w, h ){
this.width = w ; // 長方形の幅
this.height = h ; // 長方形の高さ
}
function getArea_Rectangle(){
return this.width * this.height ;
}
// インスタンスメソッドの定義
Rectangle.prototype.getArea = getArea_Rectangle ;
// この関数が Rectangle クラスのクラスメソッドになる
function getAreaMax_Static_Rectangle( p, q ){
return Math.max( p.getArea(), q.getArea() );
}
// クラスメソッドの定義
Rectangle.getAreaMax = getAreaMax_Static_Rectangle ;
// インスタンス生成
var r0 = new Rectangle( 3, 2 );
var r1 = new Rectangle( 4, 6 );
status = Rectangle.getAreaMax( r0, r1 ); // 結果は 24
同じようにクラス名のあとにメソッド名を書けばよいです。
function SuperClass(){
...
}
function SubClass(){
...
}
SubClass.prototype = new SuperClass ;
prototype オブジェクトって便利ですね(笑)new SuperClass の後ろに括弧がないのは new SuperClass() と書いたのと同じで、new 演算子の特例です。引数がないときにこのように省略できます。
function SuperClass( p, q ){
this.p = p ;
this.q = q ;
}
function SubClass( p, q ){
this.constructor( p, q );
}
SubClass.prototype = new SuperClass ;
constructor 関数を使用すればこのように簡潔にかけます(Javaのsuperみたいに使用してます)。
constructor 関数は作成元の関数を返すので、これを見ると prototype プロパティの働きが少しは分かるのではないでしょうか。
// これが Rectangle クラスの toString メソッドになる
function toString_Rectangle(){
return "長方形 : "+this.width+"×"+this.height ;
}
function Rectangle( w, h ){
this.width = w ;
this.height = h ;
}
Rectangle.prototype.toString = toString_Rectangle ;
var r0 = new Rectangle( 3, 4 );
// 表示
status = r0 ; // 結果は????
これを実行すると何がステータスバーに表示されると思いますか?
function valueOf_Rectangle(){
return this.width*this.height ;
}
function Rectangle( w, h ){
this.width = w ;
this.height = h ;
}
Rectangle.prototype.valueOf = valueOf_Rectangle ;
var r0 = new Rectangle( 3, 4 );
status = r0-0 ; // 結果は 3*4-0 で 12
-0 を付加しているのはこれが数であることを明示的にするためです。toString メソッドと valueOf メソッドのどちらが呼び出されるかは用途によって違います。toSring メソッドを呼び出したいときは r0+"", valueOf メソッドを呼び出したいときは r0-0 などのようにするか、r0.toString() などのように明示的に記すしか方法はありません。
set,setWidth,setHeightgetWidth,getHeightequalsgetAreagetDigomax、小さい方を返す minscalehalftoArrayvalueOftoStringgetAveArea
// コンストラクタの定義
//
// ----- 宣言の形式 -----
//
// new Rectangle()
// width = height = 1.0 ;
//
// new Rectangle( Rectangle r )
// width = r.width, height = r.height ;
//
// new Rectangle( number n ) : おまけ
// width = height = n ;
//
// new Rectangle( number w, number h )
// width = w, height = h ;
function Rectangle(){
var a = arguments ; // 引数
// 引数の個数により、処理を分岐
switch( a.length ){
// 引数が0個のとき
case 0 : default : this.width = this.height = 1.0 ; break ;
// 引数が1つのとき
case 1 : {
// 引数の型が Rectangle
if( a[0].constructor == Rectangle ){
this.width = a[0].width ;
this.height = a[0].height ;
}else{
this.width = this.height = a[0];
}
break ;
}
// 引数が2つのとき
case 2 : {
this.width = a[0];
this.height = a[1];
break ;
}
}
}
// 関数内でメソッドを定義することにより、限りなくバッティングを減らすことが可能になる。
// この関数内でさらに関数を定義できるということにも注意。
// 今回、全てのメソッド定義には無名関数(関数リテラル)を使用している。
function setBasicMember__Rectangle__(){
var R = Rectangle ;
var RP = R.prototype ; // メソッドが多い場合に便利
/************* クラスメソッド *************/
R.getAveArea = function(){
var s = 0 ;
var a = arguments ;
for(var i=0;i<a.length;i++) s += a[i].getArea();
return s/a.length ;
};
/********** インスタンスメソッド **********/
RP.set = function(){
var a = arguments ;
var r ;
switch( a.length ){
case 0 : default : r = new Rectangle(); break ;
case 1 : r = new Rectangle( a[0] ); break ;
case 2 : r = new Rectangle( a[0], a[1] ); break ;
}
// 新しく生成し、そのインスタンスから値を参照する。
// このテクニックはファイルサイズを減らしたいとき有効
this.width = r.width ;
this.height = r.height ;
};
// 以下のようにメソッドに複数の名前を与えることも可能
RP.setWidth = RP.setW = function( w ){ this.width = ( w ? w : 1.0 ); };
RP.setHeight = RP.setH = function( h ){ this.height = ( h ? h : 1.0 ); };
RP.getWidth = RP.getW = function(){ return this.width ; };
RP.getHeight = RP.getH = function(){ return this.height ; };
// このようなクラスの場合、equals メソッドを定義しておくのが定石
RP.equals = function( r ){
return ( ( this.width == r.width ) && ( this.height == r.height ) );
};
RP.getArea = function(){ return this.width * this.height ; };
// 対角線の長さを求める式は √( width^2 + height^2 )
RP.getDigo = function(){
return Math.sqrt( Math.pow( this.width, 2 )+Math.pow( this.height, 2 ) );
};
RP.max = function(){ return Math.max( this.width, this.height ); };
RP.min = function(){ return Math.min( this.width, this.height ); };
RP.scale = function( v ){
this.width *= v ;
this.height *= v ;
};
// 他メソッドをメソッド内で使用することも勿論、可能
RP.half = function(){ this.scale( 0.5 ); };
RP.toArray = function(){ return new Array( this.width, this.height ); };
RP.valueOf = function(){ return ( this.width == this.height ); };
RP.toString = function(){ return "Rectangle : "+this.width+" × "+this.height ; };
} setBasicMember__Rectangle__(); // 関数の呼び出し
ユーザ定義オブジェクトの作成例については Class Library のページもあるので、そちらも参考にして下さい。