= [[ProjectName]] Wiki = [[ProjectName]] のWikiページへようこそ。 = Class.js の機能 = == 開発の経緯 == '''Javascriptには、十分に機能する「オブジェクト指向」が実装されていません。''' 現在のJavaScriptのオブジェクト指向的な機能は、既存の仕様の上に、後から追加する形で導入されているため、「とって付けた」感が否めない不完全なものにとどめられています。 とくに、クラスという概念がなく、これを関数の拡張とプロトタイプの実装で代用したり、データのアクセス制御(public, protected, private)や、仮想メンバー、抽象メンバーがサポートのサポートがないことで、本来のオブジェクト指向の果実は、その大半が欠落しているといわざるをえません(ここで言う『'''メンバー'''』とは、オブジェクトのメソッドとプロパティの総称です)。 また、スーパークラスへのアクセスにも、単に他クラスのプロトタイプを参照する(実際のスーパークラスかどうかは関係なく)だけにとどまっているため、『継承』の魅力は損なわれ、新たなバグの混入のリスクと背中合わせに陥っています。 さらに、Javaにおける多重継承の別解ともいえるインターフェースもないので、オブジェクトの多態性を利用した安全なコーディングも保証されません。 この他、抽象クラスによる、メソッドやプロパティの未定義の検出、仮想メンバーによるオーバーライドの制御、クラスメンバー定義といった、一般のオブジェクト指向では当たり前に利用されているものすら存在しません(コードを駆使して似たようなことはできますが、そのコード自体が可読性を著しく崩壊させるという犠牲との取引になってしまいます)。 そもそも、上記に挙げた(一部でありますが)オブジェクト指向言語の標準機能は、とくに、大規模なアプリケーションを構築する上では、余分な条件分岐のコードを減らし、未定義値へのアクセスや未定義メソッドの呼び出しという単純ミスを回避するなど、きわめて効果的なものばかりです。 「'''Class.js'''」は、これらの機能を盛り込むと同時に、コードの表現そのものもシンプルにし、パフォーマンスの向上と、コーディング・デバッグ作業の単純化を目指した開発環境を提供するものです。 なお、Class.jsは'''Node.js'''のモジュールにも利用できます。 == 主な機能 == === (1) オブジェクトのクラス化 ==== '''Class.cast'''メソッドにObjectクラスのインスタンスを渡すことによって、インスタンスはクラス化され、定義されているプロパティは、クラスのメンバーとなります。 (以下、このインスタンスを「'''クラス定義オブジェクト'''」と呼びます。) {{{ var ClassA = Class.cast({ ................................... propertyA: ....., ................................... }); }}} === (2) 既存クラスからの継承 (Extends) === '''クラス定義オブジェクト'''中の「'''Extends'''」プロパティに既存のクラスを指定することで、クラス定義オブジェクトに他のクラス(基本クラス)を継承することができます。 継承はあくまで'''クラス間'''で行われます。'''コンストラクタ’’’は必要がなければ、定義しなくても構いません。自動的にデフォルトのコンストラクタ(引数なし)が定義されます。 継承は、単一のクラスからのみです。また、基本クラスと重複するプロパティがある場合は、エラーとなります。--> __Virtual__ {{{ var ClassB = Class.cast({ ................................... Extends: ClassA, ................................... }); }}} '''従来のJavascriptのクラスの継承は、プロトタイプ(prototypeプロパティ)へ、スーパークラスのインスタンスをコピーすることで行われます。その決定的な欠陥は、そのままでは、メソッドだけでなく、スーパークラスのプロパティまでがクラス全体で共有されてしまう点です。 {{{ // ClassXの定義 function ClassX(...) { .............................. this.data = new Array(); .............................. .............................. }; // ClassYの定義 function ClassY(...) { .............................. .............................. .............................. } // ClassXからの継承 ClassY.prototype = new ClassX(...); // インスタンスを継承するので、メソッドだけでなく、プロパティの実体も含めてプロトタイプに取り込んでしまう。 var y0 = new ClassY(...); var y1 = new ClassY(...); // y0.dataとy1.dataは、同じ配列を指してしまう。 }}} 上記の例では、インスタンス'''y0'''と'''y1'''は、共通の配列(data)をプロパティとして持ってしまいます。 そもそも、インスタンスごとに同じ内容のメソッドを重複してもつことで、無駄なメモリを消費しないように考えらたはずのプロトタイプの機構が、オブジェクトの状態を表すプロパティ(データ)をも取り込んで共有させてしまうという致命的な欠陥を潜在させてしまったのです。 これを避けて、それぞれのインスタンスが独自のプロパティをもてるようにするには、コンストラクタ内で、スーパークラス(ClassX)のコンストラクタを呼び出さなければなりません。 これにより、アクセス時に、プロトタイプ内に存在する共有のdataプロパティよりも先に、それぞれのインスタンス内のdataプロパティが検索されるようになります。 本来、スーパークラスのコンストラクタは、プロパティの初期化が必要な場合にだけ呼び出せばいいものです。単にプロパティをコピーするための呼び出しは、'''不毛なコード'''と言われても仕方ありません。 {{{ // ClassYの定義 function ClassY(...) { .............................. ClassX.call(this, ...); // スーパークラスのコンストラクタを呼び出すことでインスタンス内に値を取り込む。 .............................. } }}} こうした回避方法が'''適切でない'''さらなる理由は、うっかり別のクラスのコンストラクターを呼び出しても、'''エラー扱いにはならない'''ということです。 これは、スーパークラスが机上の概念にとどまっていて、言語仕様に'''実装されていない'''からです。 クラスとは、プログラマによる『'''新たな型'''』の定義です。これによって、コードの解析時にミスが厳格に検出されところに大きな利点があります。ところが、JavaScriptでは、クラスの定義を'''関数で代用'''してしまい、そのことでクラスの定義が『型』の定義とは無縁になってしまい、ひいては継承関係の検査ができないというツケが回っているといわざるを得ません。 もちろん、'''instanceof演算子'''を駆使して、検査コードを書き起こす方法も理論上はありえますが、やはり'''『不毛なコード』'''です。 '''Class.js'''は、このような問題からは完全に無縁といってよいほど距離を置いています。 '''Extends'''指定によってスーパークラスは明示的に関連付けられるとともに、スーパークラスのコンストラクタを呼び出さなくても、プロパティは直接、インスタンスに継承されるようになります。 また、スーパークラスのコンストラクタを呼び出す際も、クラス名を改めて指定してミスを誘発するリスクを排除し、後述する'''「superClass()」'''メソッドの利用で、確実に呼び出せるようになります。--> __superClass()__ === (3) インターフェースの実装 (Implements) === '''クラス定義オブジェクト'''中の「'''Implements'''」プロパティに'''空メソッドを定義したオブジェクト'''、または、その配列を指定することで、Javaと同様のインターフェースの機能をもつことができます。 {{{ var InterfaceA = { funcA: null // メソッドは空でよい }; var InterfaceB = { funcB: null }; }}} {{{ var ClassA = Class.cast({ ................................... ................................... Implements: [InterfaceA, InterfaceB], ................................... ................................... funcA: function(...) { // 未定義だとエラーとなります .............................. .............................. }, ................................... funcB: function(...) { // 未定義だとエラーとなります .............................. .............................. }, ................................... ................................... ................................... }); }}} これにより、'''クラス定義オブジェクト'''は、インターフェース内に空で定義されているメソッドを、実体として定義することが強制されます。 インターフェースは、複数指定できます。また、あるクラスのインスタンスが特定のインターフェースを実装しているかどうかは、クラス・メソッド「'''interfaceOf'''」で調べることができます。 {{{ var a = new ClassA(.....); if ( Class.interfaceOf(a, InterfaceA) ) { ........................ ........................ ........................ } else { throw new Error("Instance must implement "InterfaceA" interface."); } }}} 外部のインスタンスのメソッドを呼び出す際、期待するメソッドが定義されているか、個別に判定する検査ルーティンを省略できることは、大きなメリットです。 これができないと、期待するプロパティの存在を確認し、それが「関数」であるかを判断しなければなりません。しかし、それだけでは、単に「同じ名前のメソッドもっている」ことしか知ることができません。 インターフェースが実装されていることは、それを期待するクラスからのアクセスを、コード上に'''明示'''することになり、クラス間の連携にまつわるデバッグから大幅に開放されることになります。 === (4) 固定クラス・メンバー (Const) === '''クラス定義オブジェクト'''中の「'''Const'''」プロパティ内に定義されたプロパティ、メソッドは、クラス・スコープとなり、かつ値の変更や削除ができなくなります。 {{{ var ClassA = Class.cast({ ................................... ................................... Const: { CLASS_VALUE: XXXXXXXXX, ,...................... MethodX: function(...) { ,...................... ,...................... }, ,...................... }, ................................... }); ClassA.CLASS_VALUE = YYYYYYYY; // strictモードでは例外が発生。 delete ClassA.CLASS_VALUE // strictモードでは例外が発生。 ClassA.MethodX = function(...) { // strictモードでは例外が発生。 ............................ ............................ }; delte ClassA.MethodX; // strictモードでは例外が発生。 }}} === (5) クラス・メンバー (Static) === '''クラス定義オブジェクト'''中の「'''Static'''」プロパティ内に定義されたプロパティは、クラス・スコープとなります。Constとは異なり、値の変更やメソッドの差し替え、削除が可能です。 {{{ var ClassA = Class.cast({ ................................... ................................... Static: { CLASS_DATA1: *********, ,...................... CLASS_METHOD1: function(...) { .......................... .......................... }, ............................... }, ................................... ................................... var x = ClassA.CLASS_DATA1; ................................... ClassA.CLASS_METHOD1(...); ................................... ................................... }); }}} === (6) コンストラクタ (newInstance) === コンストラクタは、'''クラス定義オブジェクト'''中に「'''newInstance'''」という名称の関数として定義します。 コンストラクタ内では、「'''this.superClass()'''」メソッドによって、上位のクラス(スーパー・クラス)のコンストラクタを呼び出すことができます。その上位クラスで「'''this.superClass()'''」が実行されると、さらに上位のクラスへと連鎖します。 {{{ var ClassB = Class.cast({ ................................... Extends: ClassA, ................................... newInstance: function(...) { ............................... this.superClass(...); // スーパー・クラス"ClassA"のコンストラクタ呼び出し ............................... }, ................................... }); }}} '''O'RELLYのJavasScrit'''では、3階層以上の継承があると、上位クラスへの呼び出しが直接の親以上に遡らず、永久ループになる例が挙げられています。 これは'''A=>B=>C'''という継承が行われた場合、クラスCのインスタンスからスーパークラスBのスーパー・コンストラクタを呼び出せても、あくまで実体がクラスCのインスタンスである以上、クラスBのコンストラクタからクラスAにたどり着くことはできず、繰り返しクラスBのコンストラクタを呼び出し続けることになるからです。当然、クラスBのインスタンスであれば、クラスAにはたどり着けます。 '''Class.js'''では、この問題は解決されており、継承が何重に行われても、確実に連鎖的にスーパークラスを遡るようになっています。 === (7) プライベート・メンバー (Private) === '''クラス定義オブジェクト'''中の「'''Private'''」プロパティ内に定義されたプロパティは、外部からの直接アクセスができなくなります。 C++やJavaでは、プライベート・メンバーのアクセス制限は単純ですが、Javascriptという足かせのあるClasss.jsでは、値とメソッドとで扱いが異なります。 '''(a) 値の場合'''[[BR]] 直接のアクセスができなくなる代わりにゲッター・セッターが自動的に作成されます。名称は、プロパティ名の先頭を大文字にした上で、頭に「get」、「set」がつけられたものとなります。 下記の場合、ゲッター「getPrivateA()」と「setPrivateA()」が自動的に作成されます。 {{{ var ClassA = Class.cast({ ................................... Private: { privateA: "....", privateX: function(...) { ........................... ........................... }, ............................... }, ................................... }); }}} {{{ var a = new ClassA(...); ....................................... a.setPrivateA("....."); // セッターの実行 ....................................... var x = a.getPrivateA(); // ゲッターの実行 ....................................... }}} '''(b) メソッドの場合'''[[BR]] プライベート・メソッドは、完全に外部から隠蔽されます。これは、通常のプロトタイプ・チェーンに配置されないことを意味します。 {{{ var a = new ClassA(...); ....................................... ....................................... a.privateX(...); // privateXは、ClassAのプロトタイプに存在しないのでエラーになります。 }}} そのため、プライベート・メソッドの使用は、後述する「Loader()」内で定義するメソッドに限定されます。 その他の、公開されているメソッドからは呼び出せません。 {{{ var ClassA = cast({ ................................... Private: { ............................... privateX: function(...) { ........................... ........................... }, ............................... publicX: function(...) { ........................... this.privateX(...); // エラーになります。 ........................... } ............................... ............................... }, ................................... Loader: function() { var privateX = Class.find(this, "privateX"); // プライベート・メソッドを取得 Class.attach(this, "usePrivate", function() { // プライベート・メソッドを使用するパブリック・メソッドを定義 ...................................... privateX.call(this, ............); // プライベート・メソッドの呼び出し ...................................... }); } }); var a = new ClassA(...); a.usePrivate(); // パブリック・メソッドの中でプライベート・メソッドが使用されます。 a.privateX(); // エラーになります。 }}} プライベート・メンバーには以下のデメリットや不完全さが残されます。 *・ゲッター/セッターの名前は自動的に決まるので、既存のプロパティ名との衝突を避けるようにしなければならない。 *・ゲッター/セッターは外部に公開されるので、完全な意味での「隠蔽」にはならない。 *・クラス内でもゲッター/セッターを通してしかアクセスできなくなる。 *・既存のメソッドの中からは呼びせない。 *・記法が、通常のメソッドの大きく異なる。。 プライベート・メンバーの使用は、クラス内にある重要な情報をブラウザの開発ツールで安易に覗けないようにしたり、簡単なコードで書き換えたりできなくすることで、「バグや不正な改変を減少」させることが第一の目的です。 データの重要度やパフォーマンスを考えながら、慎重に検討した上で効果的に利用されることを推奨します。 }}} === (8) 仮想メンバー (Virtual) === '''クラス定義オブジェクト'''中の「'''Virtual'''」プロパティ内に定義されたプロパティは、このクラスを継承したサブ・クラスでオーバーライドする(同名のメソッドやプロパティで上書きする)ことができます。 Javaでは、すべのメソッドは仮想メソッドですが、'''Class.js'''では、このブロックによって明示的に指定します。[[BR]] その理由は、継承先のクラスで「誤って」同じプロパティ名を使用した場合、それを検証する方法がないと、デバッグの負担が増大することになりかねないからです。 {{{ var ClassA = Class.cast({ ................................... Virtual: { .............................. methodA: function(...) { .................... }, .............................. .............................. }, ................................... methodB: function(...) { ............................... }, ................................... }); }}} {{{ var ClassB = Class.cast({ Extends: ClassA, // ClassAを継承 ................................... methodA: function(...) { // OK 仮想メソッドなのでオーバーライド可能 ............................... this.superMethod(this.methodA, ...); // 基本クラスのメソッド呼び出し。 ............................... }, ................................... ................................... methodB: function(...) { // ERROR 基本クラスとの名前の衝突の例外が発生する。 ............................... }, ................................... }); }}} 仮想メンバーは、一度定義されると、何重に継承されても仮想メンバーのままです。[[BR]] 後述する「抽象メンバー」とは異なり、『実体』を持ちますが、「継承先でカスタマイズができる」ことを示しています。[[BR]] これは、クラスを設計する上で重要なことです。[[BR]] 仮想メンバーと非仮想メンバーとに、アクセス・パフォーマンスに差はありません。 === (9) 抽象メンバー (Abstract) === '''クラス定義オブジェクト'''中の「'''Abstract'''」プロパティ内に定義されたプロパティが存在すると、クラスは「'''抽象クラス'''」となります。[[BR]] 抽象クラスは、そのままインスタンス化することはできず、必ず継承してサブ・クラスを通して利用します。 抽象メンバーは(基本的にメソッド)は、実体を持ちません。これらは、継承先のクラスで実体を定義しなければなりません。[[BR]] 抽象メンバーが未定義だと例外が発生し、クラス自体の構築が中断されます。 {{{ var ClassA = Class.cast({ ................................... Abstract: { .............................. methodA: null, // 実体は定義しない .............................. .............................. }, .................................. }); }}} {{{ var ClassB = Class.cast({ Extends: ClassA, // 抽象クラスClassAを継承 ................................... methodA: function(...) { // 未定義だとクラス構築時に例外が発生 ............................... ............................... }, ................................... }); }}} 抽象メンバーは、一度、直接の継承クラスで定義されると、あとは仮想メンバーと同じ効果を持ちます(常にオーバーライド可能)。 抽象クラスと、インターフェースの違いは、インターフェースはすべてのプロパティは「'''実体をもたない'''」のに対し、抽象クラスでは「一部を穴あき」にした状態になっている点です。 たとえば、「図形」を表現するクラスでは「描画」メソッドは抽象メソッドにし、実際の描画処理は、サブクラスの「三角」、「円弧」、「四角」で定義する(ことが義務付けられる)というように使用します。 === (10) クラスローダー (Loader) === '''クラス定義オブジェクト'''中の「'''Loader'''」プロパティに定義されたメソッド(引数なし)は、Class.cast()メソッドによるクラス構築の最終段階で一度だけ実行されます。[[BR]] クラスの構築時には、アプリケーションもインスタンスも存在しません。したがって、クラスローダーの役割は、「'''クラス・メンバー(Static)中の『値』の初期化'''」となります。[[BR]] その対象は、自クラスにとどまらず、クラス・メソッドを通して、構築済みの他クラスも含まれます。 {{{ var ClassA = Class.cast({ ................................... Static: { .............................. propertyA: null, .............................. setPropertyA: function(...) { this.propertyA = ...; }, .............................. }, ................................... }); ....................................... ....................................... var ClassB = Class.cast({ ................................... Static: { ................................... propertyB: null, ................................... setPropertyB: function(...) { this.propertyB = ...; }, ................................... }, ................................... ................................... Loader: function() { ............................... var x = ....................... this.setPropertyB(x); // ClassBはこの時点で未定義なのでthisでアクセス ............................... var y = ....................... ClassA.setPropertyA(y); // ClassAは構築済みなので、アクセス可能 ............................... }, ................................... }); }}} === (11) プロトタイプのインポート (Imports) === '''クラス定義オブジェクト'''中の「'''Imports'''」プロパティで、外部のオブジェクトまたはその配列を指定すると、それらのプロパティが、クラス定義オブジェクトに取り込まれます。 取り込み方法には以下の2つのルールがあります。[[BR]] '''(a) クラス定義オブジェクトのプロパティをオーバーライドすることはできない。'''[[BR]] '''(b) インポートされるオブジェクト間では、後で指定した方のプロパティが優先される。'''[[BR]] {{{ var ObjectX = { ................................... property0: ....., ................................... method0: function(...) { ............................... ............................... }, ................................... }; ....................................... var ObjectY = { ................................... property1: ....., ................................... method0: function(...) { // ObjectXのmethod0は、取り込まれない ............................... ............................... }, ................................... method1: function(...) { ............................... ............................... }, ................................... }; ....................................... var ClassA = Class.cast({ ................................... Imports: [ObjectX, ObjectY], ................................... method1: function(...) { // ObjectYのmethod1は、取り込まれない ............................... ............................... }, ................................... }); }}} インポート機能は、一見、『'''多重継承'''』のように見えますが、プロパティの取り込み後、もとのオブジェクトの存在は不可視となります。 また、名前の衝突の可能性が高いので、再利用性も低くなります。さらに、衝突の結果、どのオブジェクトのプロパティが採用されたかも、判別が困難です。使用には、細心の注意が必要といわざるを得ません。 '''Class.js'''では、Importsの使用は、積極的に推奨しません。継承やインターフェースを活用するか、クラスのインスタンスとして取り込むか、後述する'''内部クラス'''として定義するなど、別の方法を考慮した方がよいでしょう。 === (12) 内部クラス === '''クラス定義オブジェクト'''中のプロパティの値を「'''Class.cast()'''」メソッドの呼び出しにすることで、クラス内にクラスを構築することができます。 内部クラスには、以下の2通りがあり、それぞれ利用方法が異なります。[[BR]] '''(a) Staticプロパティ内で定義'''[[BR]] 内部クラスは外側のクラスを通じて公開され、外部からアクセスしてインスタンスの生成が可能となります。[[BR]] 外側のクラスとの関連が密接であり、従属関係をコードで表すことで、アプリケーション自体のメンテナンスを容易にするなどの効果が期待できます。 {{{ var ClassA = Class.cast({ ................................... Static: { ............................... InnerClassA: Class.cast({ // 公開内部クラス ........................... newInstance: function(...) { ....................... ....................... }, ........................... innerMethodA: function(...) { ....................... ....................... }, ........................... }); ............................... getInnerAInstance: function(...) { // クラス・メソッドを通して生成 return new this.InnerClassA(...); }, ............................... }, ................................... }); ....................................... // インスタンスの生成 var ia0 = new ClassA.InnerClassA(....); var ia1 = ClassA.getInnerAInstance(....); // メソッド呼び出し ia0.innerMethodA(...); ia1.innerMethodA(...); }}} '''(b) 通常のプロパティで定義'''[[BR]] このクラスは、外側のクラスのインスタンスの生成時に構築され、当然、そのインスタンスからしかアクセスできません。ただし、生成された内部クラスのインスタンスは、通常のインスタンス同様に外部から操作が可能です。[[BR]] この手法は、従属関係の明示化とともに、外部からのインスタンス生成を防ぐことでバグの混入を回避する有効な手段となります。 {{{ var ClassB = Class.cast({ ................................... InnerClassB: Class.cast({ // 非公開内部クラス ............................... newInstance: function(...) { ........................... ........................... }, ............................... innerMethodB: function(...) { ........................... ........................... }, ............................... }), ................................... methodB: function(...) { ,................................. var ib = new this.InnerClassB(...); // インスタンスからのみ生成可能 ,................................. ib.innerMethodB(); ,................................. }, ................................... getInnerBInstance: function(...) { // インスタンスメソッドを通して生成 return new this.InnerClassB(...); }, ................................... }); ....................................... // インスタンスの生成 var b = new ClassB(...); var ib = b.getInnerBInstance(...); // メソッド呼び出し ib.innerMethodB(...); }}} 非公開内部クラスは、外側のクラスのインスタンスごとに構築されるので、そのインスタンスを大量に生成するようなアプリケーションでは避けるべきです。 外側のクラスがシングルトン(システムで1インスタンス)のようなケースで、そこから内部クラスのインスタンスを一括して管理するような仕様に最適です。 === (13) superClass()メソッドとsuperMethod()メソッド === 他のクラスを継承したクラスには、自動的に「'''superClass()'''」と「'''superMethod()'''」メソッドが定義されます。 '''superClass()'''は、コンストラクタ内でのみで使用し、直接のスーパークラスのコンストラクタを呼び出します。 一方、'''superMethood()'''は、オーバーライドされたスーパークラスの仮想メソッドを実行します。--> __Virtual__ {{{ var ClassA = Class.cast({ ................................... // コンストラクタ newInstance: function(...) { ............................... ............................... }, ................................... Virtual: { ............................... methodX: function(...) { ........................... ........................... }, ............................... }, ................................... }); ....................................... }}} {{{ var ClassB = Class.cast({ Extends: ClassA, ................................... // コンストラクタ newInstance: function(...) { ............................... // ClassAのコンストラクタを呼び出す // thisがサブ・クラスのインスタンスでもClassAへの遡及は保証される。 this.superClass(...); ............................... }, ................................... methodX: function(...) { // ClassAのmethodXがVirtualなのでオーバーライド可能 ............................... // ClassAのインスタンス・メソッドを呼び出す(第1引数は、自身のメソッド) this.superMethod(this.MethodX, ...); ............................... }, ................................... }); ....................................... }}} {{{ var ClassC = Class.cast({ Extends: ClassB, ................................... // コンストラクタ newInstance: function(...) { ............................... // ClassBのコンストラクタを呼び出す this.superClass(...); ............................... }, ................................... methodX: function(...) { // ClassAの系譜にあるのでオーバーライド可能 ............................... // ClassBのインスタンス・メソッドを呼び出す。 // 連鎖的に、ClassBのmethodX()も実行される。 this.superMethod(this.methodX, ...); ............................... }, ................................... }); ....................................... }}} === (14) Beanクラス === BeanとはJavaBeansの仕様を参考にしたものです。JavaBeansでは、その仕様にのっとって作成された部品クラスのことを指しますが、Class.jsでは、オブジェクト中のすべての値をプライベート・メンバーにしたクラスにしたものを、こう呼びます。[[BR]] 具体的には、'''Class.bean'''メソッドにObjectクラスのインスタンスを渡すことによって、クラスを構築します。[[BR]] 定義されている値のプロパティは、すべてプライベート・メンバーとなり、'''Privateプロパティ'''のルール(上記参照)にしたがって、ゲッター/セッターがメンバーごとに定義され、さらに引数なしのコンストラクタが定義されます。[[BR]] 一方、メソッドについては、そのまま外部からのアクセス可能なメソッドとして機能しますが、値のメンバーに対しては、アクセサ経由でしかアクセスできなくなります。 {{{ BeanX = Class.bean({ valueA: ....., valueB: ..... ................................... methodA: function(...) { ............................... this.valueA = ....; // ERROR valueAは隠蔽されるので直接アクセス禁止 ............................... this.setValueA(....); // OK valueAへはアクセサ経由でアクセス ............................... }, ................................... }); ....................................... }}} 上記の例では、valueA、valueBは、プライベート・メンバーとなって外部からのアクセスが遮断され、アクセサ「'''getValueA()'''」、「'''setValueA()'''」、「'''getValueB()'''」、「'''setValueB()'''」、および'''引数なしのコンストラクタ'''が定義されます。 {{{ var x = new BeanX(); x.setValueA(...); // 値はアクセサ経由 x.setValueB(...); // 値はアクセサ経由 ....................................... ....................................... x.methodA(...); // メソッドは呼び出し可能 ....................................... ....................................... console.log("value A is --> " + x.getValueA()); console.log("value B is --> " + x.getValueB()); ....................................... }}} セキュリティ性の高いクラスが生成できる点と、アクセサの定義作業を省略できる点で、Beanはデータを保持するクラスに適しているといえます。 Beanクラスのクラス定義オブジェクトでは、'Extends'、 'Implements'、 'Static'などの特殊なブロックは、それらのルールが示す効果を持たず、単なるプライベート・メンバーとみなされます(アクセサが定義されてしまいます)。 === (15) Node.jsで利用する場合 === Node.jsで使用する際は、以下のように'''require()'''によって取り込みます。 {{{ ......................................................... ......................................................... var Class = require("[パス]/Class-XX.XX.XX-min.js").main; ......................................................... ......................................................... var MyClass = Class.cast({ ..................................................... ..................................................... ..................................................... }); ..................................................... ..................................................... var my_obj = new MyClass(...); ..................................................... ..................................................... }}}