X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F01_core%2F13_XClass.js;h=61749575a131da3789531ec0a6c1a18be8f91f72;hb=ef25747bebf1799d49f9bd0d64e339da9ea61d13;hp=072bc2cd40be838a8969243cfaff2e9d76b9a4e2;hpb=54eba266c29baf743f6ac67942a2788cd697b12d;p=pettanr%2FclientJs.git diff --git a/0.6.x/js/01_core/13_XClass.js b/0.6.x/js/01_core/13_XClass.js index 072bc2c..6174957 100644 --- a/0.6.x/js/01_core/13_XClass.js +++ b/0.6.x/js/01_core/13_XClass.js @@ -15,7 +15,7 @@ var * クラス名 * @type {string} */ - name : '' + NAME : '' }, X_Class_CLASS_LIST = [], @@ -25,7 +25,10 @@ var X_Class_traits = null, X_Class_useObjectCreate = false, // !!Object.create, http://jsperf.com/prototype-vs-object-create-perf // Opera Mobile 12.10 Android11 IS01 でクラスのメンバが欠落する問題に遭遇。__proto__ を辞めると動作,,, - X_Class_use_proto_ = !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] && !!X_emptyFunction.prototype.__proto__, + X_Class_use_proto_ = !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] && + // Android で原因不明のエラーに遭遇しているのは、この辺りが怪しい... 2016.3.9 + !X_UA[ 'AOSP' ] && !X_UA[ 'ChromeWV' ] && + !!X_emptyFunction.prototype.__proto__, X_Class_constructorFix = X_UA[ 'AOSP' ] < 3 || X_UA[ 'iOS' ] < 5, X_Class_SEAL_KILLING = [], @@ -36,14 +39,14 @@ X_Class_CommonMethods = * 全ての動的メンバを削除して、インスタンスを破棄する。
* インスタンスが X.EventDispatcher とそのサブクラスの場合、次の動作をする。 *
    - *
  1. X.Event.BEFORE_KILL_INSTANCE を発火する。戻り値のビットフラグに X_Callback.PREVENT_DEFAULT が立つ場合、破棄をキャンセルし X.Event.KILL_INSTANCE_CANCELED を発火する。この間に kill() が呼ばれても無視される。 + *
  2. X.Event.BEFORE_KILL_INSTANCE を発火する。戻り値のビットフラグに X.Callback.PREVENT_DEFAULT が立つ場合、破棄をキャンセルし X.Event.KILL_INSTANCE_CANCELED を発火する。この間に kill() が呼ばれても無視される。 *
  3. 破棄に進む場合は、X.Event.KILL_INSTANCE を発火する。 *
  4. dispatch 中は、インスタンスの全ての dispatch が終了するまで実際の破棄を待つ。 *
  5. 実際の破棄では、インスタンスのメンバの削除に加えて全てのイベントリスナを解除する。 */ // TODO kill したインスタンスのイベントが残っていないか?これは開発用のみ 'kill' : function(){ - var listeners, flag, p, timers, def; + var listeners, flag, p, i, list, timers, def; // TODO 破棄済のインスタンスへの kill @@ -57,7 +60,7 @@ X_Class_CommonMethods = // listeners がない場合、イベントの登録がないため、BEFORE_KILL_INSTANCE は呼ばれない。 // KILL_RESERVED == true の場合、BEFORE_KILL_INSTANCE は呼ばれない。 if( listeners && !listeners[ X_LISTENERS_KILL_RESERVED ] && listeners[ X_EVENT_BEFORE_KILL_INSTANCE ] ){ - X_Class_SEAL_KILLING[ X_Class_SEAL_KILLING.length ] = this; + X_Class_SEAL_KILLING[ i = X_Class_SEAL_KILLING.length ] = this; if( this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE ) & X_CALLBACK_PREVENT_DEFAULT ){ this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE_CANCELED ); @@ -67,7 +70,7 @@ X_Class_CommonMethods = X_Class_SEAL_KILLING.length === 1 ? ( X_Class_SEAL_KILLING.length = 0 ) : - X_Class_SEAL_KILLING.splice( X_Class_SEAL_KILLING.indexOf( this ), 1 ); + X_Class_SEAL_KILLING.splice( X_Class_SEAL_KILLING[ i ] === this ? i : X_Class_SEAL_KILLING.indexOf( this ), 1 ); if( flag ) return; }; @@ -79,17 +82,26 @@ X_Class_CommonMethods = }; if( listeners[ X_EVENT_KILL_INSTANCE ] ){ - X_Class_SEAL_KILLING[ X_Class_SEAL_KILLING.length ] = this; + X_Class_SEAL_KILLING[ i = X_Class_SEAL_KILLING.length ] = this; listeners[ X_LISTENERS_KILL_RESERVED ] = false; this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE ); X_Class_SEAL_KILLING.length === 1 ? ( X_Class_SEAL_KILLING.length = 0 ) : - X_Class_SEAL_KILLING.splice( X_Class_SEAL_KILLING.indexOf( this ), 1 ); + X_Class_SEAL_KILLING.splice( X_Class_SEAL_KILLING[ i ] === this ? i : X_Class_SEAL_KILLING.indexOf( this ), 1 ); + }; + + if( !( listeners = this[ '_listeners' ] ) ){ + for( p in listeners ){ + //if( X_EMPTY_OBJECT[ opt_type ] ) continue; + if( p <= X_LISTENERS_KILL_RESERVED ) continue; + list = listeners[ p ]; + for( i = list.length; i; ){ + this[ 'unlisten' ]( p, list[ --i ] ); + }; + }; }; - - X_EventDispatcher_unlistenAll( this ); }; if( this[ 'instanceOf' ]( Node ) ){ @@ -119,7 +131,7 @@ X_Class_CommonMethods = }, /** - * 関数は Constructor 内で使用します。クラス定義を辿ってスーパークラスのコンストラクタを探します。
    + * 関数は Constructor 内で使用します。クラス定義を辿ってスーパークラスのコンストラクタを呼び出します。
    * 内部的には、呼び出したコンストラクタは配列に控え(X_Class_CALLING_SUPER)、呼び出したコンストラクタ内でさらに Super が呼ばれた場合、配列を元にさらにスーパーなコンストラクタを辿ります。 * @example Constructor : function( arg1, arg2 ){ * this.Super( aeg1, arg2 ); @@ -130,24 +142,22 @@ X_Class_CommonMethods = // TODO 現在 new しているインスタンスを保持してチェックする 'Super' : function( var_args ){ var me = this, - sClass = me, + sClass = me.constructor, i = X_Class_SUPER_CALLER.indexOf( me ), stack, t, def, ret; if( i === -1 ){ X_Class_SUPER_CALLER[ i = X_Class_SUPER_CALLER.length ] = me; - X_Class_SUPER_STACKS[ i ] = stack = 0; + t = stack = X_Class_SUPER_STACKS[ i ] = 0; } else { - stack = X_Class_SUPER_STACKS[ i ]; + t = stack = X_Class_SUPER_STACKS[ i ]; + + while( t ){ + sClass = X_Class_getClassDef( sClass ).SuperClass; + --t; + }; }; - - t = stack; - - while( t ){ - sClass = X_Class_getClassDef( sClass ).SuperClass; - --t; - }; - + while( sClass ){ ++t; sClass = X_Class_getClassDef( sClass ).SuperClass; @@ -161,7 +171,12 @@ X_Class_CommonMethods = }; }; - if( X_Class_SUPER_STACKS[ i ] === stack ) console.log( 'スーパークラス、またはスーパークラスのコンストラクタは存在しません' ); + // index が替わっている可能性があるので取り直し + if( X_Class_SUPER_CALLER[ i ] !== me ) i = X_Class_SUPER_CALLER.indexOf( me ); + + if( X_Class_SUPER_STACKS[ i ] === stack ){ + //console.log( 'スーパークラス、またはスーパークラスのコンストラクタは存在しません' ); + }; if( stack === 0 ){ X_Class_SUPER_CALLER.splice( i, 1 ); @@ -173,25 +188,27 @@ X_Class_CommonMethods = }, /** - * func について、親クラスで設定されている同名の関数メンバーを呼び出す。
    - * 第一引数にオーバーライド済の自身の(自身から参照できる)関数を指定します。内部では関数名を調べた上で prototype チェーンをゴリゴリ辿る、特別なことはしていません。 - * superCall がネストする場合、arguments.callee でないと正しく現在階層を取得して親関数を知ることができない - * 次の理由によって、関数名で辿ることはやめました + * myFunc について、スーパークラスで設定されている同名の関数を呼び出す。
    + * 低速な関数なので多用されるべきではありません!
    + * 第一引数に自身の(自身から参照できる)関数を指定します。内部では関数名を調べた上で prototype チェーンをゴリゴリ辿る、特別なことはしていません。
    + * superCall と Super がネストする場合も現在のクラス階層を X_Class_SUPER_CALLER, X_Class_SUPER_STACKS を使って控えているので、意図した親関数が呼ばれます。
    + * 次の理由によって、関数名で辿ることは非推奨です。 *
      *
    1. closur compiler でメソッド名が変更される - *
    2. superCall 内からさらに superCall が呼ばれた場合に、起点となる関数を特定できない *
    - * 次の場合、意図した動作が得られません + * 次の場合、意図した動作が得られません。 *
      - *
    1. 2つ以上の異なる名前で同じ関数がメンバーがいた場合
      - *
    2. または、サブクラスのメンバーにスーパークラスと同じ関数が出現する - *
    3. superCall 以外の手段で親関数を呼び、そのなかで superCall を読んだ + *
    4. 2つ以上の異なる名前で同じ関数がメンバーがいた場合 + *
    5. サブクラスの prototype にスーパークラスと同じ関数をコピーしている + *
    6. 非関数でメンバーを上書きしている + *
    7. superCall 以外の手段で親関数を呼び、そのなかで superCall を呼んだ *
    - * 通常の X.Class.create の書き方ではこのような状況は起きませんが、js はなんでもできるので - * 参考:ES5なJavascriptでモダンなクラス的継承&スーパー呼び出し + * 通常の X.Class.create の書き方ではこのような状況は起きませんが、js はなんでもいろいろ出来てしまいますから…
    + * 参考:ES5なJavascriptでモダンなクラス的継承&スーパー呼び出し
    + * original:Classical Inheritance in JavaScript * @param myFunc {Function|string} オーバーライド済の自身の(自身から参照できる)関数。 * @param var_args {...*} オーバーライド元関数に渡す任意の数の引数 - * @example return this.superCall( arguments.callee, param0, param1, ... ); + * @example return this.superCall( this.myFunc, param0, param1, ... ); * @return {*} オーバーライド元の関数を呼び出した戻り値。 */ 'superCall' : function( myFunc, var_args ){ @@ -199,9 +216,8 @@ X_Class_CommonMethods = sClass = me.constructor, proto = sClass.prototype, i = X_Class_SUPER_CALLER.indexOf( me ), - l, d, ret, args = arguments, - name, p, sFunc; + p, name, stack, t, sFunc, ret; if( X_Type_isFunction( myFunc ) ){ for( p in proto ){ @@ -211,24 +227,25 @@ X_Class_CommonMethods = }; }; if( !name ) return; + } else + if( X_Type_isString( myFunc ) && X_Type_isFunction( me[ myFunc ] ) ){ + name = myFunc; } else { return; }; if( i === -1 ){ X_Class_SUPER_CALLER[ i = X_Class_SUPER_CALLER.length ] = me; - X_Class_SUPER_STACKS[ i ] = stack = 0; + t = stack = X_Class_SUPER_STACKS[ i ] = 0; } else { - stack = X_Class_SUPER_STACKS[ i ]; + t = stack = X_Class_SUPER_STACKS[ i ]; + + while( t ){ + sClass = X_Class_getClassDef( sClass ).SuperClass; + --t; + }; }; - t = stack; - - while( t ){ - sClass = X_Class_getClassDef( sClass ).SuperClass; - --t; - }; - if( sClass ){ myFunc = sClass.prototype[ name ]; @@ -238,21 +255,20 @@ X_Class_CommonMethods = sFunc = sClass.prototype[ name ]; if( sFunc !== myFunc /* X_Object_own( name, sClass.prototype ) */ ){ - // this の関数と異なり、値が設定されていたら、今は手を抜いて undef か?見ている、正しくは hasOwnProperty if( X_Type_isFunction( sFunc ) ){ X_Class_SUPER_STACKS[ i ] += t; switch( args.length ){ - case 0 : - ret = sFunc.call( me ); - break; case 1 : - ret = sFunc.call( me, args[ 0 ] ); + ret = sFunc.call( me ); break; case 2 : - ret = sFunc.call( me, args[ 0 ], args[ 1 ] ); + ret = sFunc.call( me, args[ 1 ] ); break; case 3 : - ret = sFunc.call( me, args[ 0 ], args[ 1 ], args[ 2 ] ); + ret = sFunc.call( me, args[ 1 ], args[ 2 ] ); + break; + case 4 : + ret = sFunc.call( me, args[ 1 ], args[ 2 ], args[ 3 ] ); break; default : args = X_Array_copy( args ); @@ -266,6 +282,9 @@ X_Class_CommonMethods = }; }; + // index が替わっている可能性があるので取り直し + if( X_Class_SUPER_CALLER[ i ] !== me ) i = X_Class_SUPER_CALLER.indexOf( me ); + if( stack === 0 ){ X_Class_SUPER_CALLER.splice( i, 1 ); X_Class_SUPER_STACKS.splice( i, 1 ); @@ -285,6 +304,7 @@ X_Class_CommonMethods = // TODO instanceof に対応したブラウザはそちらを使用 'instanceOf' : function( klass ){ var Super = this; + if( this.constructor === klass ) return true; while( Super = X_Class_getClassDef( Super ).SuperClass ){ if( Super === klass ) return true; @@ -297,7 +317,7 @@ X_Class_CommonMethods = // --- interface ----------------------------------------------------------- // // ------------------------------------------------------------------------- // -/** +/* * @enum {number} * @const */ @@ -337,6 +357,7 @@ X[ 'Class' ] = /** @lends X.Class */ { */ 'NONE' : X_Class.NONE, + // TODO この指定、フレームワーク内だけ! /** * インスタンスは破棄時(this.kill())に回収され、次回の new MyClass() 時に再利用されます。 * @const @@ -464,7 +485,7 @@ X[ 'Class' ] = /** @lends X.Class */ { klass.prototype.constructor = klass; }; - klass[ 'name' ] = displayName; + klass[ 'NAME' ] = displayName; if( opt_abstract ){ classDef.isAbstract = true; @@ -647,7 +668,7 @@ function X_Class_actualConstructor( f, args ){ obj = def.Constructor ? def.Constructor.apply( instance, args ) : - instance[ 'Super' ].apply( instance, args ); + def.SuperClass && instance[ 'Super' ].apply( instance, args ); if( obj !== instance && ( X_Type_isObject( obj ) || X_Type_isFunction( obj ) ) ){ // Class instance[ 'kill' ]();