X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F01_core%2F13_XClass.js;h=dc1166b8c3a3c03e71644ed1cea7290709f64ec9;hb=35daae003b3b017a92d0c883f120bf3baf604fba;hp=bf86574923631465c6272bde1399de8a513e198c;hpb=427c4cbb7f8b1ee674b845ade0796fcbfee0fcba;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 bf86574..dc1166b 100644 --- a/0.6.x/js/01_core/13_XClass.js +++ b/0.6.x/js/01_core/13_XClass.js @@ -22,10 +22,13 @@ var 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__, + X_Class_constructorFix = X_UA[ 'AOSP' ] < 3 || X_UA[ 'iOS' ] < 5, X_Class_SEAL_KILLING = [], X_Class_CommonMethods = @@ -128,14 +131,15 @@ X_Class_CommonMethods = */ // TODO 現在 new しているインスタンスを保持してチェックする 'Super' : function( var_args ){ - var sClass = this, - i = X_Class_CALLING_SUPER.indexOf( sClass ), + var me = this, + sClass = me, + i = X_Class_CALLING_SUPER.indexOf( me ), l, sList, def, sConst, ret; if( i === -1 ){ - X_Class_CALLING_SUPER[ l = X_Class_CALLING_SUPER.length ] = sClass; + X_Class_CALLING_SUPER[ l = X_Class_CALLING_SUPER.length ] = me; X_Class_CALL_SUPER_STACK[ l ] = sList = []; - def = X_Class_getClassDef( sClass ); + def = X_Class_getClassDef( me ); if( !def.Constructor ) sClass = def.SuperClass;// 現在のクラスがコンストラクタを持たない場合 SuperConstructor を new で呼んでいるため再び呼ばないようにする } else { sList = X_Class_CALL_SUPER_STACK[ i ]; @@ -148,13 +152,13 @@ X_Class_CommonMethods = if( !sConst ) break; if( sList.indexOf( sConst ) === -1 ){ sList[ sList.length ] = sConst; - ret = sConst.apply( this, arguments ); + ret = sConst.apply( me, arguments ); --sList.length; if( !sList.length ){ X_Class_CALLING_SUPER.splice( i, 1 ); X_Class_CALL_SUPER_STACK.splice( i, 1 ); }; - return ret; + return ret || me; }; }; console.log( 'スーパークラスのコンストラクタが見つかりません' ); @@ -162,20 +166,38 @@ X_Class_CommonMethods = /** * func について、親クラスで設定されている同名の関数メンバーを呼び出す。
- * 第一引数に関数を指定し、2つ以上の異なる名前で同じ関数がメンバーがいた場合、動作が不確実になります。
+ * 第一引数にオーバーライド済の自身の(自身から参照できる)関数を指定します。内部では関数名を調べた上で prototype チェーンをゴリゴリ辿る、特別なことはしていません。 + * superCall がネストする場合、arguments.callee でないと正しく現在階層を取得して親関数を知ることができない + * 次の理由によって、関数名で辿ることはやめました + *
    + *
  1. closur compiler でメソッド名が変更される + *
  2. superCall 内からさらに superCall が呼ばれた場合に、起点となる関数を特定できない + *
+ * 次の場合、意図した動作が得られません + *
    + *
  1. 2つ以上の異なる名前で同じ関数がメンバーがいた場合
    + *
  2. または、サブクラスのメンバーにスーパークラスと同じ関数が出現する + *
  3. superCall 以外の手段で親関数を呼び、そのなかで superCall を読んだ + *
+ * 通常の X.Class.create の書き方ではこのような状況は起きませんが、js はなんでもできるので * 参考:ES5なJavascriptでモダンなクラス的継承&スーパー呼び出し - * @param funcNameOrFunc {Function|string} スーパークラスの関数名 または、オーバーライド済の自身の関数。 + * @param myFunc {Function|string} オーバーライド済の自身の(自身から参照できる)関数。 * @param var_args {...*} オーバーライド元関数に渡す任意の数の引数 * @example return this.superCall( arguments.callee, 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 ), + l, d, ret, args = arguments, - name, p, sFunc, hit = false; - if( X_Type_isFunction( funcNameOrFunc ) ){ - for( p in this.constructor.prototype ){ - if( this.constructor.prototype[ p ] === funcNameOrFunc ){ + name, p, sFunc, hit; + + if( X_Type_isFunction( myFunc ) ){ + for( p in proto ){ + if( proto[ p ] === myFunc ){ name = p; break; }; @@ -184,41 +206,76 @@ X_Class_CommonMethods = } else { return; }; + + if( i === -1 ){ + X_Class_SUPER_CALLER[ l = X_Class_SUPER_CALLER.length ] = me; + X_Class_SUPER_STACKS[ l ] = d = {}; + } else { + d = X_Class_SUPER_STACKS[ i ]; + }; - if( X_EMPTY_OBJECT[ name ] ) return; - + if( stack = d[ name ] ){ + myFunc = stack[ stack.length - 1 ]; + } else { + stack = d[ name ] = []; + }; + + /* + while( t ){ + sClass = X_Class_getClassDef( sClass ).SuperClass; + --t; + }; + mysFunc = sClass.prototype[ name ]; */ + + + // TODO while( sClass ){ - def = X_Class_getClassDef( sClass ); - sClass = def.SuperClass; - sFunc = sClass.prototype[ name ]; - if( sFunc === funcNameOrFunc ){ - hit = true; // 現在の関数にヒット + sFunc = sClass.prototype[ name ]; + + if( !hit && sFunc === myFunc ){ + hit = true; // 現在の関数にヒット, さらにスーパークラスを辿って同名のプロパティの関数が現れたらそれが目指すもの } else - if( hit && X_Object_inObject( name, this ) ){ + if( hit && sFunc !== myFunc /* X_Object_own( name, sClass.prototype ) */ ){ + // this の関数と異なり、値が設定されていたら、今は手を抜いて undef か?見ている、正しくは hasOwnProperty if( X_Type_isFunction( sFunc ) ){ + stack[ stack.length ] = sFunc; switch( args.length ){ + case 0 : + ret = sFunc.call( me ); + break; case 1 : - return sFunc.call( this ); + ret = sFunc.call( me, args[ 0 ] ); + break; case 2 : - return sFunc.call( this, args[ 1 ] ); + ret = sFunc.call( me, args[ 0 ], args[ 1 ] ); + break; case 3 : - return sFunc.call( this, args[ 1 ], args[ 2 ] ); - case 4 : - return sFunc.call( this, args[ 1 ], args[ 2 ], args[ 3 ] ); + ret = sFunc.call( me, args[ 0 ], args[ 1 ], args[ 2 ] ); + break; default : args = X_Array_copy( args ); args.shift(); - return sFunc.apply( this, args ); + ret = sFunc.apply( me, args ); + break; }; + --stack.length; }; break; }; + sClass = X_Class_getClassDef( sClass ).SuperClass; + }; + + if( !stack.length ) delete d[ name ]; + if( X_Object_isEmpty( d ) ){ + X_Class_SUPER_CALLER.splice( l, 1 ); + X_Class_SUPER_STACKS.splice( l, 1 ); }; + return ret; }, /** * インスタンスのクラスか?またはスーパークラスか?調べる。
- * instanceof 構文をサポートしない環境(IE4,Mac IE5)を想定する場合、必ずこのメソッドを使用すること。
+ * instanceof 構文をサポートしない環境(IE5以下)を想定する場合、必ずこのメソッドを使用すること。
* クラスのインスタンスか?だけ調べたい場合は this.constructor === klass が高速。 * @param klass {__ClassBase__} クラス定義 * @return {boolean} @@ -429,16 +486,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 ) ); @@ -584,7 +638,7 @@ function X_Class_actualConstructor( f, args ){ def.live && def.live.push( instance ); - if( ( X_UA[ 'AOSP' ] < 3 || X_UA[ 'iOS' ] < 5 ) && instance.constructor !== klass ){ + if( X_Class_constructorFix && instance.constructor !== klass ){ console.log( '------- constructor の不一致!' ); // Android2.3.7 instance.constructor = klass; };