ECMAScript / ECMA-262 Edition 5.1ベースのJavaScript文法
オブジェクト指向とは、その特性(プロパティ)と値のセットの一定の集合である対象物(オブジェクト)を中心とした考え方及びアルゴリズムであり、オブジェクトは、そのプロパティを含むオブジェクト自身をコピー、または、当該オブジェクトを参照することによって利用し、そのコピーをインスタンス、その派生、引き継ぎの流れを継承、JavaScriptではインスタンス化とも呼びます。
また、同じオブジェクト指向と言ってもJavaやC++はクラスベース、JavaScriptはプロトタイプベースという違いがあります。
【クラスベースとプロトタイプベースのイメージ】
【クラスベース】
クラスライブラリ
|_スーパークラス
|_サブクラス
| |_サブクラス
|_サブクラス
|_サブクラス
【プロトタイプベース】
オブジェクト
|_プロパティ(メソッド)
|_prototype プロパティ
|_プロパティ(コンストラクタ)
|_プロパティ(他メソッド)
|_プロパティ(値)
オブジェクト指向におけるクラスとは、クラスライブラリのことを指し、よってクラスベースとは、ライブラリとしてクラスの集合を持ち、これらクラスを基底クラス、大元になるクラスをスーパークラス、スーパークラスを派生させた(extends)新たなクラス(インスタンスであるサブクラス)を作成してスーパークラスのプロパティ、メソッドを継承、更にサブクラスからサブクラスを派生させることもできます。
一方、プロトタイプとは、原型の意であり、プロトタイプベースとは、ある原型を基にする考え方で且つ、ライブラリを持つのではなく、プロパティとその値のセットから成る集合であるオブジェクトを原型とするものでJavaScriptにおいては、各種の基本となるオブジェクトを原型とし、その内、Global/Math/JSON以外のオブジェクトは、プロトタイプベースの継承プロパティと共有プロパティを実装する為に使用される prototype というプロパティを持ちます。
クラスベースのJavaやC++では、クラス内に定義した関数をメソッドと呼び、インスタンス生成に際し、初期化を行うのは、クラス名と同名のメソッドであり、クラス名ではなく、その同名のメソッド名をコンストラクタと呼びます。
list1 = new Array() ;
list2 = Array() ;
/*
プロトタイプベースのJavaScriptでは
呼ばれるのはメソッドではなく
オブジェクト自身
Arrayは、newの有無に関わらず
インスタンスを生成
*/
一方、プロトタイプベースのJavaScriptでは、関数をプロパティとして持たせることもでき、このプロパティとして持つ関数をメソッド(関数・メソッド)と呼び、インスタンスを生成する際には当該オブジェクトの内部メソッドによって初期化されますが、new演算子を伴うか否かは別としてコンストラクタとなる内部メソッドを隠蔽してオブジェクト自体を呼び出すことで自動的にその初期化を行う内部メソッドが呼ばれる為、JavaScriptでは、そのメソッドではなく、インスタンスを生成可能なオブジェクト自体をコンストラクタとも呼びます。
JavaScriptのプロパティへのアクセス方法には、2通りあり、下記は、その内のドット記法を利用しています。
// Object.prototype の設定値
document.write('Object.prototype is ' + Object.prototype + "<br>" ) ;
// >> Object.prototype is [object Object]
// Object.prototype.constructor の設定値
document.write('Object.prototype.constructor is ' + Object.prototype.constructor + "<br>" ) ;
// >> Object.prototype.constructor is function Object() { [native code] }
// Objectオブジェクトが持つプロパティ名一覧
document.write('Object.getOwnPropertyNames(Object) is ' + Object.getOwnPropertyNames(Object) + "<br>" ) ;
// >> Object.prototype.constructor is prototype,getPrototypeOf,getOwnPropertyDescriptor,keys,defineProperty,defineProperties,create,getOwnPropertyNames,isExtensible,preventExtensions,freeze,isFrozen,seal,isSealed,length,name,arguments,caller
// Object.prototypeオブジェクトが持つプロパティ名一覧
document.write('Object.getOwnPropertyNames(Object.prototype) is ' + Object.getOwnPropertyNames(Object.prototype) + "<br>" ) ;
// >> Object.prototype.constructor is constructor,toSource,toString,toLocaleString,valueOf,watch,unwatch,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,__defineGetter__,__defineSetter__,__lookupGetter__,__lookupSetter__
...etc.
インスタンスの場合、prototypeオブジェクト(prototypeもプロパティを持つ為、オブジェクト)自体は継承されるわけではなく、prototypeオブジェクトが持つ各種プロパティが継承され、初期には、1つの constructor プロパティとその他のプロパティを持ち、このconstructorプロパティに設定されたメソッドが自動的に呼ばれるコンストラクタに当たり、これによって初期化されるので、このメソッドを差し替えることでオーバーライドすることも可能です。
【クラスベースとプロトタイプベース比較イメージ】
クラス
|
継承
|
サブクラス
クラス
|
継承
|
サブクラス
オーバーライド
あるクラスを継承し、
メソッドを書き換える
(メソッド上書き)
オーバーロード
同一クラス内
{
method1(){}
method1(int n){}
}
同一クラス内に
引数の型、数、順の異なる
同名コンストラクタを複数配置、
汎用性を高める(多重定義)
↓
コンパイル
↓
中間コード、実行ファイル
変更不可
(除:再コンパイル)
オブジェクト obj
|_プロパティ
|_prototypeプロパティ
|
新たなプロパティ
xyz を追加
↓
obj.prototype.xyz
に値を設定
↓
t = new obj ( ) ;
t.xyz の値は、
obj.prototype.xyz
の値を継承
obj.prototype更新
(追加/削除)は
t にも動的に反映
一方、t.xyz の値含め
t を変更しても
obj.prototypeへの影響なし
初期化内容の書き換え
prototype.constructorに
初期化用メソッドを設定
↓
静的なコンパイル不要
↓
動的に常時
プロパティ・メソッド
追加・変更・削除可
クラスベースの場合、コンパイル後は、再コンパイルしない限り、プロパティの変更ができませんが、別途、静的なコンパイル(、というよりライブラリとのリンク)を要さないプロトタイプベースでは、動的にプロパティにアクセス、変更することも可能です。
インスタンスを初期化するコンストラクタにおいてクラスベースの場合、クラス名とコンストラクタ名が同名ながら別に存在するので引数の型や数の異なる同名のコンストラクタとなるメソッドを設定するオーバーロード(多重継承)も可能ですが、プロトタイプベースでは、コンストラクタとなるメソッドは、オブジェクトによって内部で自動的に呼び出され(、そのメソッド内に引数の型や数の異なるメソッドをネストすることが可能であったとしても常に最後のメソッドが実行されるだけであり)、呼び出しは、常にオブジェクト名を使用することからオブジェクト自体がコンストラクタとも呼ばれ、JavaScriptでは、オーバーロードを実装することはできません。
尚、インスタンス化に伴い、prototype オブジェクト自体は継承されず、prototypeオブジェクトの中身であるメソッド含む各種プロパティだけが継承される為、インスタンス(オブジェクト)からは、prototypeを介すことなく、直接プロパティにアクセスすることができます。