X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F01_core%2F13_XClass.js;h=a0b15cc797cedaa73579b0b9a6e47990fd7d5fae;hb=2bea17ee282c2cf4ce0de6685db3ade602f6e60f;hp=ebe050bb5e85a140cf9d77906ecc9ef8523a3ef5;hpb=24f935c7a129b0a00d800e4983da6491a913aeef;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 ebe050b..a0b15cc 100644 --- a/0.6.x/js/01_core/13_XClass.js +++ b/0.6.x/js/01_core/13_XClass.js @@ -20,12 +20,14 @@ var X_Class_CLASS_LIST = [], X_Class_DEF_LIST = [], - X_Class_CALLING_SUPER = [], - X_Class_CALL_SUPER_STACK = [], + X_Class_SUPER_CALLER = [], + X_Class_SUPER_STACKS = [], 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__, - // Opera Mobile 12.10 Android11 IS01 でクラスのメンバが欠落する問題に遭遇。__proto__ を辞めると動作,,, + X_Class_constructorFix = X_UA[ 'AOSP' ] < 3 || X_UA[ 'iOS' ] < 5, + X_Class_SEAL_KILLING = [], X_Class_CommonMethods = /** @lends __ClassBase__.prototype */ @@ -34,41 +36,85 @@ X_Class_CommonMethods = * 全ての動的メンバを削除して、インスタンスを破棄する。
* インスタンスが X.EventDispatcher とそのサブクラスの場合、次の動作をする。 *
    - *
  1. X.Event.BEFORE_KILL_INSTANCE を発火する。戻り値のビットフラグに X_Callback.PREVENT_DEFAULT が立つ場合、破棄をキャンセルし X.Event.KILL_INSTANCE_CANCELED を発火する。 + *
  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 def, listeners, p; + var listeners, flag, p, i, list, timers, def; - // TODO kill 中の kill の呼び出しを防ぐ, 破棄済のインスタンスへの kill + // TODO 破棄済のインスタンスへの kill if( this[ 'instanceOf' ]( X_EventDispatcher ) ){ - if( this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE ) & X_Callback_PREVENT_DEFAULT ){ - this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE_CANCELED ); - return; - }; - listeners = this[ '_listeners' ]; - - if( listeners && listeners[ X_LISTENERS_DISPATCHING ] ){ - listeners[ X_LISTENERS_KILL_RESERVED ] = true; - return; + + // SEAL のタイミングは、イベント中なので listeners が存在する + if( listeners && X_Class_SEAL_KILLING.length && X_Class_SEAL_KILLING.indexOf( this ) !== -1 ) return; + + // 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[ 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 ); + // BEFORE_KILL_INSTANCE, KILL_INSTANCE_CANCELED 内で kill() しても PREVENT_DEFAULT の場合はこれを無視する。 + flag = true; + }; + + X_Class_SEAL_KILLING.length === 1 ? + ( X_Class_SEAL_KILLING.length = 0 ) : + X_Class_SEAL_KILLING.splice( X_Class_SEAL_KILLING[ i ] === this ? i : X_Class_SEAL_KILLING.indexOf( this ), 1 ); + + if( flag ) return; }; + if( listeners = this[ '_listeners' ] ){// unlisten 等で listeners が破棄されている場合があるので取り直し。 + if( listeners[ X_LISTENERS_DISPATCHING ] ){ + listeners[ X_LISTENERS_KILL_RESERVED ] = true; + return; + }; + + if( listeners[ X_EVENT_KILL_INSTANCE ] ){ + 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[ 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 ] ); + }; + }; + }; + }; + + if( this[ 'instanceOf' ]( Node ) ){ + // console.log( 'KILL : ' + this.call( 'outerHTML' ) ); + X_Node_onKill( this ); + }; + + timers = X_EventDispatcher_LAZY_TIMERS; + // asyncDispatch の削除 - for( p in X_EventDispatcher_LAZY_TIMERS ){ - if( X_EventDispatcher_LAZY_TIMERS[ p ] === this ){ + for( p in timers ){ + if( timers[ p ] === this ){ // delete X_EventDispatcher_LAZY_TIMERS[ p ]; コレ不要 X_Timer_remove( p ); }; }; - - this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE ); - listeners && X_EventDispatcher_unlistenAll( this ); }; X_Object_clear( this ); @@ -82,7 +128,7 @@ X_Class_CommonMethods = }, /** - * 関数は Constructor 内で使用します。クラス定義を辿ってスーパークラスのコンストラクタを探します。
    + * 関数は Constructor 内で使用します。クラス定義を辿ってスーパークラスのコンストラクタを呼び出します。
    * 内部的には、呼び出したコンストラクタは配列に控え(X_Class_CALLING_SUPER)、呼び出したコンストラクタ内でさらに Super が呼ばれた場合、配列を元にさらにスーパーなコンストラクタを辿ります。 * @example Constructor : function( arg1, arg2 ){ * this.Super( aeg1, arg2 ); @@ -92,95 +138,162 @@ X_Class_CommonMethods = */ // TODO 現在 new しているインスタンスを保持してチェックする 'Super' : function( var_args ){ - var sClass = this, - i = X_Class_CALLING_SUPER.indexOf( sClass ), - n = -1, - l, sList, def, sConst, ret; + var me = this, + sClass = me.constructor, + i = X_Class_SUPER_CALLER.indexOf( me ), + stack, t, def, ret; if( i === -1 ){ - X_Class_CALLING_SUPER[ l = X_Class_CALLING_SUPER.length ] = sClass; - X_Class_CALL_SUPER_STACK[ l ] = sList = []; + X_Class_SUPER_CALLER[ i = X_Class_SUPER_CALLER.length ] = me; + t = stack = X_Class_SUPER_STACKS[ i ] = 0; } else { - sList = X_Class_CALL_SUPER_STACK[ i ]; + t = stack = X_Class_SUPER_STACKS[ i ]; + + while( t ){ + sClass = X_Class_getClassDef( sClass ).SuperClass; + --t; + }; }; - + while( sClass ){ + ++t; + sClass = X_Class_getClassDef( sClass ).SuperClass; + if( !sClass ) break; def = X_Class_getClassDef( sClass ); - sClass = def.SuperClass; - sConst = def.SuperConstructor; - if( sConst && sList[ ++n ] !== sConst ){ - sList[ n ] = sConst; - ret = sConst.apply( this, arguments ); - --sList.length; - if( !sList.length ){ - X_Class_CALLING_SUPER.splice( i, 1 ); - X_Class_CALL_SUPER_STACK.splice( i, 1 ); - }; - return ret; + + if( def.Constructor ){ + X_Class_SUPER_STACKS[ i ] += t; + ret = def.Constructor.apply( me, arguments ); + break; }; }; - 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 ); + X_Class_SUPER_STACKS.splice( i, 1 ); + } else { + X_Class_SUPER_STACKS[ i ] = stack; + }; + return ret || me; }, /** - * func について、親クラスで設定されている同名の関数メンバーを呼び出す。
    - * 第一引数に関数を指定し、2つ以上の異なる名前で同じ関数がメンバーがいた場合、動作が不確実になります。
    - * 参考:ES5なJavascriptでモダンなクラス的継承&スーパー呼び出し - * @param funcNameOrFunc {Function|string} スーパークラスの関数名 または、オーバーライド済の自身の関数。 + * myFunc について、スーパークラスで設定されている同名の関数を呼び出す。
    + * 低速な関数なので多用されるべきではありません!
    + * 第一引数に自身の(自身から参照できる)関数を指定します。内部では関数名を調べた上で prototype チェーンをゴリゴリ辿る、特別なことはしていません。
    + * superCall と Super がネストする場合も現在のクラス階層を X_Class_SUPER_CALLER, X_Class_SUPER_STACKS を使って控えているので、意図した親関数が呼ばれます。
    + * 次の理由によって、関数名で辿ることは非推奨です。 + *
      + *
    1. closur compiler でメソッド名が変更される + *
    + * 次の場合、意図した動作が得られません。 + *
      + *
    1. 2つ以上の異なる名前で同じ関数がメンバーがいた場合 + *
    2. サブクラスの prototype にスーパークラスと同じ関数をコピーしている + *
    3. 非関数でメンバーを上書きしている + *
    4. superCall 以外の手段で親関数を呼び、そのなかで superCall を呼んだ + *
    + * 通常の 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( funcNameOrFunc, var_args ){ - var sClass = this, + 'superCall' : function( myFunc, var_args ){ + var me = this, + sClass = me.constructor, + proto = sClass.prototype, + i = X_Class_SUPER_CALLER.indexOf( me ), args = arguments, - name, p, sFunc, hit = false; - if( X_Type_isFunction( funcNameOrFunc ) ){ - for( p in this.constructor.prototype ){ - if( this.constructor.prototype[ p ] === funcNameOrFunc ){ + p, name, stack, t, sFunc, ret; + + if( X_Type_isFunction( myFunc ) ){ + for( p in proto ){ + if( proto[ p ] === myFunc ){ name = p; break; }; }; if( !name ) return; + } else + if( X_Type_isString( myFunc ) && X_Type_isFunction( me[ myFunc ] ) ){ + name = myFunc; } else { return; }; - - if( X_EMPTY_OBJECT[ name ] ) return; - - while( sClass ){ - def = X_Class_getClassDef( sClass ); - sClass = def.SuperClass; - sFunc = sClass.prototype[ name ]; - if( sFunc === funcNameOrFunc ){ - hit = true; // 現在の関数にヒット - } else - if( hit && X_Object_inObject( name, this ) ){ - if( X_Type_isFunction( sFunc ) ){ - switch( args.length ){ - case 1 : - return sFunc.call( this ); - case 2 : - return sFunc.call( this, args[ 1 ] ); - case 3 : - return sFunc.call( this, args[ 1 ], args[ 2 ] ); - case 4 : - return sFunc.call( this, args[ 1 ], args[ 2 ], args[ 3 ] ); - default : - args = X_Object_cloneArray( args ); - args.shift(); - return sFunc.apply( this, args ); + + if( i === -1 ){ + X_Class_SUPER_CALLER[ i = X_Class_SUPER_CALLER.length ] = me; + t = stack = X_Class_SUPER_STACKS[ i ] = 0; + } else { + t = stack = X_Class_SUPER_STACKS[ i ]; + + while( t ){ + sClass = X_Class_getClassDef( sClass ).SuperClass; + --t; + }; + }; + + if( sClass ){ + myFunc = sClass.prototype[ name ]; + + while( sClass ){ + ++t; + sClass = X_Class_getClassDef( sClass ).SuperClass; + sFunc = sClass.prototype[ name ]; + + if( sFunc !== myFunc /* X_Object_own( name, sClass.prototype ) */ ){ + if( X_Type_isFunction( sFunc ) ){ + X_Class_SUPER_STACKS[ i ] += t; + switch( args.length ){ + case 1 : + ret = sFunc.call( me ); + break; + case 2 : + ret = sFunc.call( me, args[ 1 ] ); + break; + case 3 : + 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 ); + args.shift(); + ret = sFunc.apply( me, args ); + break; + }; }; + break; }; - break; }; }; + + // 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 ); + } else { + X_Class_SUPER_STACKS[ i ] = stack; + }; + return ret; }, /** * インスタンスのクラスか?またはスーパークラスか?調べる。
    - * instanceof 構文をサポートしない環境(IE4,Mac IE5)を想定する場合、必ずこのメソッドを使用すること。
    + * instanceof 構文をサポートしない環境(IE5以下)を想定する場合、必ずこのメソッドを使用すること。
    * クラスのインスタンスか?だけ調べたい場合は this.constructor === klass が高速。 * @param klass {__ClassBase__} クラス定義 * @return {boolean} @@ -240,6 +353,7 @@ X[ 'Class' ] = /** @lends X.Class */ { */ 'NONE' : X_Class.NONE, + // TODO この指定、フレームワーク内だけ! /** * インスタンスは破棄時(this.kill())に回収され、次回の new MyClass() 時に再利用されます。 * @const @@ -300,7 +414,7 @@ X[ 'Class' ] = /** @lends X.Class */ { * ); */ function X_Class_create( /* displayName, classSetting, privateClass, props */ ){ - var args = X_Object_cloneArray( arguments ), + var args = X_Array_copy( arguments ), displayName = args[ 0 ], classSetting, opt_pool, opt_abstract, opt_final, @@ -346,7 +460,7 @@ X[ 'Class' ] = /** @lends X.Class */ { classDef.Constructor = props[ 'Constructor' ]; }; - klass = X_Callback_actualClosure( cbHash ); // TODO callbackHash を class定義の置き場所にしてしまう!なるほど… + klass = X_Closure_actualClosure( cbHash ); // TODO callbackHash を class定義の置き場所にしてしまう!なるほど… cbHash.klass = klass; klass[ 'superClassOf' ] = X_Class_superClassOf; klass[ 'subClassOf' ] = X_Class_subClassOf; @@ -370,7 +484,7 @@ X[ 'Class' ] = /** @lends X.Class */ { klass[ 'name' ] = displayName; if( opt_abstract ){ - classDef.Abstract = true; + classDef.isAbstract = true; } else if( opt_pool ){ classDef.pool = []; @@ -391,16 +505,13 @@ X[ 'Class' ] = /** @lends X.Class */ { function X_Class_getClass( instance ){ - var cList = X_Class_CLASS_LIST, - i = cList.length, - klass; - for( ; i; ){ - klass = cList[ --i ]; - if( instance.constructor === klass ) return klass; - }; + var cList = X_Class_CLASS_LIST, i; + + if( ( i = cList.indexOf( instance.constructor ) ) !== -1 ) return cList[ i ]; if( cList.indexOf( instance ) !== -1 ) return instance; }; +// TODO def = klass( X_Closure_COMMAND_BACK ) function X_Class_getClassDef( KlassOrInstance ){ var i = X_Class_CLASS_LIST.indexOf( KlassOrInstance ); if( i === -1 ) i = X_Class_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) ); @@ -462,12 +573,11 @@ function X_Class_subClassOf( klass ){ * @example var SubClass = SuperClass.inherits( 'Sub', X_Class.FINAL, { ... } ); * @param {string} [displayName] クラスの名前 * @param {number} [classSetting=0] X_Class.POOL_OBJECT | X_Class.FINAL など - * @param {__ClassBase__=} [privateClass] このクラスとペアで動作するシャドウクラス * @param {object} [props={}] このクラスのメンバと関数。コンストラクタは Constructor と書くこと * @return {__ClassBase__} */ -function X_Class_inherits( /* displayName, classSetting, opt_PrivateClass, props */ ){ - var args = X_Object_cloneArray( arguments ), +function X_Class_inherits( /* displayName, classSetting, props */ ){ + var args = X_Array_copy( arguments ), params = [], Super = this, superDef = X_Class_getClassDef( Super ), @@ -519,8 +629,8 @@ function X_Class_inherits( /* displayName, classSetting, opt_PrivateClass, props def = X_Class_getClassDef( klass ); // 継承用プロパティを控える def.SuperClass = Super; - def.SuperProto = Super.prototype; - def.SuperConstructor = superDef.Constructor || superDef.SuperConstructor; + //def.SuperProto = Super.prototype; + //def.SuperConstructor = superDef.Constructor || superDef.SuperConstructor; return klass; }; @@ -534,7 +644,7 @@ function X_Class_actualConstructor( f, args ){ def = f.classDef, instance, obj; - if( def.Abstract ){ + if( def.isAbstract ){ X.Logger.critical( 'AbstractClass!' ); return; }; @@ -544,18 +654,23 @@ function X_Class_actualConstructor( f, args ){ X_Class_useObjectCreate ? Object.create( klass.prototype ) : new klass( X_Closure_COMMAND_DROP ); - + def.live && def.live.push( instance ); + if( X_Class_constructorFix && instance.constructor !== klass ){ + console.log( '------- constructor の不一致!' ); // Android2.3.7 + instance.constructor = klass; + }; + obj = def.Constructor ? def.Constructor.apply( instance, args ) : - def.SuperConstructor && - def.SuperConstructor.apply( instance, args ); + def.SuperClass && instance[ 'Super' ].apply( instance, args ); if( obj !== instance && ( X_Type_isObject( obj ) || X_Type_isFunction( obj ) ) ){ // Class instance[ 'kill' ](); return obj; }; + return instance; };