Javascript のクラス定義

はじめに

javascript は基本的にプアな環境でも動くオブジェクト指向言語として設計されている.

Java のような(僕がよく触っているのは Actionscript3 )クラスが無いので大規模開発には向いていない.

しかし実際には大規模開発が行われていてその手法を見ていくと、なんとなくクラスっぽいこともできるよ、という性質が寄与している.

以下の文書はそんな理解の僕が書いてますよ.鵜呑みにしないでよ.

prototype 拡張

このなんとなくクリスタル、じゃなくて、なんとなくクラス の実現に寄与しているのが prototype 拡張という性質になる. これを使って一度定義した関数や値をインスタンスやサブクラスのインスタンスから何度も再利用している. 結果メモリ効率が良くなる. しかしこのままでは、プライベートな値を持てなかったり、各インスタンスが好き勝手にクラスの定義を書き換えたりもできてしまう.

最初に書くもの

大規模な js をフレームワークから書く場合、開発者はまず var Class = /* クラス定義 */ から書くことになるのだと思う.(または、最初の規模感があいまいで、コードがでかくなった段階でいよいよクラス定義を追加する場合でも、位置的にはコードの最初に書いて読み込まれるようになるはず.)

プライベートな関数を作れないことを良しとするか?性能を犠牲にしてプライベートを作るか?その場合、どの程度強固にプライベートなプロパティを実装するか?これは結局のところ各開発者の性癖で決まる.

その性癖にしたがってまずは、オレオレクラス定義を書くことになる.

system.js のクラスシステム

機能

  • 継承、アブストラクトクラス、ファイナル(クラスレベル)
  • サブクラスインスタンス instanceof スーパークラス が true を返すようにする.
  • system API から受け取ったインスタンスのプライベートな値には system の外からは触れないようにする.
  • _proto_ をフリーズできれば、クラス定義に悪さできないんじゃね?程度の意識、、、
  • アプリケーションが定義したクラスをチェックして、アプリケーションのシャットダウンで全クラスとインスタンスを削除する.
  • .kill() で全ての ダイナミックプロパティを削除した上でプールに格納し、new ではプールからオブジェクトを容易する.

だいたい以上のことができればいいな、ということが version 0.5.x まで書いてきて判明してきました. そして以上の機能を持ったクラス定義コードを書いたのでした.

クラス種類・設定表

種類 Abstractfinalプールプライベート用クラスの使用Super
通常クラス 可(1) 無し
通常サブクラス 可   可(1) 可(2)
プライベート用クラス 可(1) 無し 無し
プライベート用サブクラス可(1) 無し
  • (1) Abstractクラスのプールは常に無し.但し、有りを指定してもエラーにならない.
  • (2) サブクラスのプール、プライベートクラス指定が無い場合、スーパークラスの設定を使用.
  • Abstractクラスを継承した非 Abstract クラスを継承して再び Abstract クラスを作ることも可能.
  • プライベート用クラスを継承して通常クラスを定義する、またはその逆 はできない.
  • Abstract なデータ用クラスはプライベート用クラスに指定できない.

書き方の例

  1. var myPrivateClass = Class.create(
  2. 'myPrivateData', // Class の名前 デバッグで役に立つ、はず、省略可
  3. CLASS.PRIVATE_DATA | Class.POOL_OBJECT, // Class の設定、省略可
  4. {
  5. Constructor : function( name, age ){
  6. this.name = name;
  7. this.age = age;
  8. }
  9. }
  10. );
  11. var myClass = Class.create(
  12. 'myExampleClass',
  13. Class.POOL_OBJECT | Class.FINAL,
  14. myPrivateClass, // プライベート用クラス、省略した場合プライベートクラスは無し
  15. { // 関数や値
  16. Constructor : function( msg, name, age ){ // コンストラクタ
  17. myClass.newPrivateData( this, name, age ); // インスタンスに対してプライベートクラスの生成
  18. this.msg = msg;
  19. },
  20. alert : function(){
  21. var data = myClass.getPrivateData( this ); // インスタンスに対応するプライベートクラスの取得
  22. window.aleart( this.msg + data.name + data.age );
  23. }
  24. });

Abstract

クラスは Abstract クラスになる.new AbsClass() とするとエラーになる.

  1. var myClass = AbsClass.inherits( { ... } );

として、サブクラスを作る.inherits() は通常クラスでもできる.

final

クラスの継承を禁止する.

プール

.kill() で全てのプロパティを削除したインスタンスを保持していて、new では保持したインスタンスを再利用する.

プライベートクラスの使用

クラス定義時にプライベート用クラスを設定する.

  1. var privateClass = Class.create( Class.PRIVATE_DATA, { props,,, } );
  2. var myClass = Class.create( Class.POOL_OBJECT, privateClass, { props,,, } );

プライベートデータの利用開始には以下のようにする.

  1. Constructor : function(){
  2. var data = myClass.newPrivateData( this, args,,, );
  3. data.print( data.message );
  4. }

一度 newPrivateData したあとは getPrivateData でアクセスする. myClass を隠蔽することでプライベートデータへのアクセスを制限できる.

Super

スーパークラスのプロパティにアクセスできるようになる.スーパークラスのコンストラクタには、書いてない、、、

  1. this.Super.alert( this.Super.defaultName );

弱点・注意点

  • プライベートデータ・インスタンスの取得に配列を探すだけの時間がかかる.
  • プールされたインスタンスへの参照をアプリケーションが維持していた場合、次回インスタンスが再利用されたときに、他のアプリケーションによって設定された値を読み出すことができてしまう.このために、アプリケーションをまたぐ インスタンスの再利用を制限する、などの追加実装が必要.(むきになるなら)