// ------------ local variables -------------------------------------------- //\r
// ------------------------------------------------------------------------- //\r
\r
-var X_Callback_LIVE_LIST = [],\r
+var /** @const */\r
+ X_Callback_LIVE_LIST = [],\r
+ /** @const */\r
X_Callback_POOL_LIST = [],\r
+ /** @const */\r
X_Closure_COMMAND_BACK = X_Callback_LIVE_LIST,\r
+ /** @const */\r
X_Closure_COMMAND_DROP = X_Callback_POOL_LIST,\r
\r
+ /** @const */\r
X_Callback_THIS_FUNC = 1,\r
+ /** @const */\r
X_Callback_HANDLEEVENT = 2,\r
+ /** @const */\r
X_Callback_FUNC_ONLY = 3,\r
\r
+ /** @const */\r
X_Callback_NONE = 0,\r
+ /** @const */\r
X_Callback_UN_LISTEN = 1,\r
- X_Callback_STOP_PROPAGATION = 2, // 上位階層への伝播のキャンセル\r
+ /** @const */\r
+ X_Callback_STOP_PROPAGATION = 2,\r
+ /** @const */\r
X_Callback_STOP_NOW = 4 | 2, // 同一階層のリスナーのキャンセル(上位へもキャンセル)\r
+ /** @const */\r
X_Callback_PREVENT_DEFAULT = 8, // 結果動作のキャンセル,\r
+ /** @const */\r
X_Callback_MONOPOLY = 16, // move event を独占する\r
+ /** @const */\r
X_Callback_SYS_CANCEL = 32 | 4 | 2;\r
\r
/*\r
*/\r
var listener;\r
\r
-/*\r
- * コールバックに thisObject や、追加の引数を設定するための情報を収めたハッシュ\r
- * @typedef {{ k : number, f : (function|undefined), x : (listener|undefined), s : (Array|undefined) }}\r
+/**\r
+ * <h4>クロージャについて</h4>\r
+ * javascript 開発で多用されるクロージャですが、次の理由によりその使用は慎重になるべきです。\r
+ * <ol>\r
+ * <li>正しく参照を切ってガベージコレクトされる条件を満たしたか?プログラマが見落としやすく、結果メモリリークを起こしやすい。特にコードがコールバック地獄になると目も当てられない。\r
+ * </ol>\r
+ * \r
+ * とくに次のようなページでは、クロージャの使用を極力避けるべきです。破棄が行われることが確実で即時関数によって新しい名前を追加したくない場合と、次項で紹介する絶対に必要なケース以外で使わないようにします。\r
+ * <ol>\r
+ * <li>ajax によりデータ取得がたびたび起こり、合わせて不要なデータの破棄も行う。\r
+ * <li>シングルページアプリケーションで画面の生成と破棄が繰り返される。\r
+ * <li>Web アプリケーションで、一度開いたら一日中操作し続けるユーザーも想定される。\r
+ * </ol>\r
+ * \r
+ * <h4>クロージャが絶対に必要な場合</h4>\r
+ * IE5 ~ IE8 の独自イベントモデルに於いて、イベントオブジェクトに event.currentTarget に相当するものがなく、現在どの HTML 要素でイベントが起こっているか分かりません。<br>\r
+ * そのため HTML 要素とコールバック関数を束縛するクロージャを使う必要があります。<br>\r
+ * 参照:『Javascript 第5版』 オライリー p430 17.3.6 attachEvent() と this キーワード<br>\r
+ * \r
+ * このほかに IE8 以下と Opera11 以下の XHR ではイベントオブジェクトが用意されないため、eventType が分からない問題があります。このために eventType とコールバック関数を束縛するクロージャが必要です。<br>\r
+ * \r
+ * このように、Web ブラウザと javascript の接点では、どうしてもクロージャが必要なケースがあります。<br>\r
+ * さて、クロージャの使用を最小限に留め、残った僅かなクロージャのメモリリークをチェックし、といくら慎重に開発を行っても、そもそもクロージャが破棄されるのか?ガベージコレクションの怪しいブラウザもあり問題はまだ続きます。\r
+ * \r
+ * <h4>再利用可能なクロージャ</h4>\r
+ * クロージャの使用を最小限にしたうえで、なおかつクロージャがガベージコレクションされない可能性を考慮して、pettanR フレームワークでは再利用可能なクロージャを用意します。<br>\r
+ * 再利用可能クロージャはフレームワーク内で生成・破棄されるため、ユーザーが直接触ることはありません。しかし debug ツールのコールスタックには登場するため、知識を持っておくことは有益です。<br>\r
+ * \r
+ * <h5>再利用可能クロージャの作成</h5>\r
+ * X_Callback_create() で再利用可能なクロージャの作成。次のパターンで呼び出します。<br>\r
+ * 最大で三つの引数を並べる一連のパターンは、 EventDispatcher.listen unlisten, listening や X.Timer.add, once でも使われますので、ここでよく目を通しておきます。\r
+ * \r
+ * <table>\r
+ * <tr><th>this コンテキスト+関数<td>X_Callback_create( thisObject, func )<td>func.call( thisObject );\r
+ * <tr><th>this コンテキスト+関数+追加引数<td>X_Callback_create( thisObject, func, [ arg1, ...args ] )<td>func.apply( thisObject, [ arg1, ...args ] );\r
+ * <tr><th>listener オブジェクト<td>X_Callback_create( listener )<td>listener.handleEvent(); コールバックに関数でなく handleEvent 関数をメンバに持つオブジェクトを渡すのは NN4 からある javascript のお約束です。\r
+ * <tr><th>listener オブジェクト+追加引数<td>X_Callback_create( listener, [ arg1, ...args ] )<td>listener.handleEvent.apply( listener, [ arg1, ...args ] );\r
+ * <tr><th>関数<td>X_Callback_create( func )<td>特別な操作は不要なので再利用可能クロージャは作られません。func をそのまま利用します。\r
+ * <tr><th>関数+引数<td>X_Callback_create( func, [ arg1, ...args ] )<td>func.apply( ?, [ arg1, ...args ] );\r
+ * </table>\r
+ * \r
+ * <h5>再利用可能クロージャの破棄と再利用</h5>\r
+ * X_Callback_correct() によってクロージャは回収され再利用に備えます。<br>\r
+ * 実は、クロージャが束縛するのは、this コンテキストやコールバック関数といった、<strong>そのもの</strong>ではなく、それらを一定のルールで格納したハッシュです。<br>\r
+ * このハッシュはクロージャに与えた後も、適宜に取得が可能です。このハッシュのメンバーを書き換えることで、クロージャの this コンテキストやコールバック関数を書き換えています。\r
+ * \r
+ * @class __CallbackHash__\r
+ * @classdesc コールバック関数に this コンテキストや、追加の引数を設定するための情報を収めたハッシュです。<br>\r
+ * フレームワークユーザは直接触ることにはないが、重要な情報なので書いておきます。\r
+ * @private\r
*/\r
-var callbackHash;\r
+var __CallbackHash__ =\r
+/** @lends __CallbackHash__.prototype */\r
+{\r
+ /**\r
+ * コールバックの種類を表す数値。 this + function, this.handleEvent, function only がある。\r
+ * @type {number} \r
+ */\r
+ kind : X_Callback_THIS_FUNC,\r
+ /**\r
+ * コールバック。\r
+ * @type {funciton|undefined} \r
+ */\r
+ func : undefined,\r
+ /**\r
+ * コールバックの this コンテキスト。 \r
+ * @type {listener|object|undefined}\r
+ */\r
+ context : undefined,\r
+ /**\r
+ * コールバックに追加する引数。イベントのコールバックでは event オブジェクトのあとに追加されるため supplement[0] が第一引数にならない点に注意。\r
+ * @type {Array|undefined}\r
+ */\r
+ supplement : undefined,\r
+ /**\r
+ * __CallbackHash__ の情報を元に、コールバックを実施するプロキシ。\r
+ * @type {Function}\r
+ */\r
+ proxy : X_Callback_proxyCallback\r
+};\r
\r
-/*\r
- * \r
- * @typedef {(funciton|{ _ : function, same : function, kill : function, a : (Array|undefined) })}\r
+/**\r
+ * X.Timer と X.EventDispatcher からのコールバックの返り値を定義。\r
+ * @namespace X.Callback\r
+ * @alias X.Callback\r
*/\r
-var functionHash;\r
-\r
-X.Callback = {\r
- NONE : X_Callback_NONE,\r
- UN_LISTEN : X_Callback_UN_LISTEN,\r
- STOP_PROPAGATION : X_Callback_STOP_PROPAGATION,\r
- STOP_NOW : X_Callback_STOP_NOW,\r
- PREVENT_DEFAULT : X_Callback_PREVENT_DEFAULT,\r
- MONOPOLY : X_Callback_MONOPOLY\r
+X[ 'Callback' ] = {\r
+ /**\r
+ * このコールバックでは返り値による操作は無い。\r
+ */\r
+ 'NONE' : X_Callback_NONE,\r
+ /**\r
+ * X.Timer, X.EventDispatcher のコールバックでタイマーやイベントリスナの解除に使用。\r
+ */\r
+ 'UN_LISTEN' : X_Callback_UN_LISTEN,\r
+ /**\r
+ * 上位階層へのイベント伝播のキャンセル。DOM イベントのコールバックの戻り値に指定すると e.stopPropagation() が呼ばれる。\r
+ */\r
+ 'STOP_PROPAGATION' : X_Callback_STOP_PROPAGATION,\r
+ /**\r
+ * 以降のイベントのディスパッチを中断する。STOP_PROPAGATION との違いは、次に控えているコールバックもキャンセルされる点。但し system によって追加されたイベントはキャンセルされない。\r
+ */\r
+ 'STOP_NOW' : X_Callback_STOP_NOW,\r
+ /**\r
+ * DOM イベントのコールバックの戻り値に指定すると e.preventDefault() が呼ばれる。\r
+ * またフレームワーク内で定義されたデフォルト動作の回避にも使用される。\r
+ */\r
+ 'PREVENT_DEFAULT' : X_Callback_PREVENT_DEFAULT,\r
+ /**\r
+ * X.UI に於いて、ポインターイベントの戻り値に指定すると、以降のポインターベントを独占する。\r
+ */\r
+ 'MONOPOLY' : X_Callback_MONOPOLY\r
};\r
\r
// ------------------------------------------------------------------------- //\r
function X_Callback_create( thisObject, opt_callback, opt_args /* [ listener || ( context + function ) || function ][ args... ] */ ){\r
var obj = X_Callback_classifyCallbackArgs( thisObject, opt_callback, opt_args ),\r
l, ret, _obj;\r
- if( !obj.k ) return obj;\r
+ \r
+ if( !obj.kind ) return obj;\r
+ \r
if( l = X_Callback_POOL_LIST.length ){\r
- ret = X_Callback_POOL_LIST[ l - 1 ]; --X_Callback_POOL_LIST.length; // ret = X_Callback_POOL_LIST.pop();\r
- _obj = ret( X_Closure_COMMAND_BACK );\r
- _obj.k = obj.k;\r
- _obj.f = obj.f;\r
- _obj.x = obj.x;\r
- _obj.s = obj.s;\r
+ ret = X_Callback_POOL_LIST[ l - 1 ]; --X_Callback_POOL_LIST.length; // ret = X_Callback_POOL_LIST.pop();\r
+ _obj = ret( X_Closure_COMMAND_BACK );\r
+ \r
+ _obj.kind = obj.kind;\r
+ _obj.func = obj.func;\r
+ _obj.context = obj.context;\r
+ _obj.supplement = obj.supplement;\r
+ _obj.proxy = X_Callback_proxyCallback;\r
} else {\r
- ret = X_Closure_actualClosure( obj );\r
+ ret = X_Callback_actualClosure( obj );\r
+ obj.proxy = X_Callback_proxyCallback;\r
};\r
X_Callback_LIVE_LIST[ X_Callback_LIVE_LIST.length ] = ret;\r
return ret;\r
};\r
\r
-function X_Closure_actualClosure( obj ){\r
- return function(){\r
- if( arguments[ 0 ] === X_Closure_COMMAND_BACK ) return obj;\r
- if( arguments[ 0 ] !== X_Closure_COMMAND_DROP ) return X_Callback_proxyCallback( obj, arguments );\r
- };\r
-};\r
\r
function X_Callback_classifyCallbackArgs( arg1, arg2, arg3, alt_context ){\r
var obj;\r
\r
- if( arg1 && X.Type.isFunction( arg2 ) ){\r
- obj = { x : arg1, f : arg2, k : X_Callback_THIS_FUNC };\r
+ if( arg1 && X_Type_isFunction( arg2 ) ){\r
+ obj = { context : arg1, func : arg2, kind : X_Callback_THIS_FUNC };\r
} else\r
- if( arg1 && X.Type.isFunction( arg1[ 'handleEvent' ] ) ){\r
- obj = { x : arg1, k : X_Callback_HANDLEEVENT };\r
+ if( arg1 && X_Type_isFunction( arg1[ 'handleEvent' ] ) ){\r
+ obj = { context : arg1, kind : X_Callback_HANDLEEVENT };\r
arg3 = arg2;\r
} else\r
- if( X.Type.isFunction( arg1 ) ){\r
+ if( X_Type_isFunction( arg1 ) ){\r
arg3 = arg2;\r
if( alt_context ){\r
- obj = { x : alt_context, f : arg1, k : X_Callback_THIS_FUNC };\r
+ obj = { context : alt_context, func : arg1, kind : X_Callback_THIS_FUNC };\r
} else {\r
- obj = { f : arg1, k : X_Callback_FUNC_ONLY };\r
+ obj = { func : arg1, kind : X_Callback_FUNC_ONLY };\r
};\r
} else\r
- if( X.Type.isFunction( arg2 ) ){\r
+ if( X_Type_isFunction( arg2 ) ){\r
//console.log( 'X_Callback_classifyCallbackArgs : arg1 が ' + arg1 + 'です' ); ie4 で error\r
if( alt_context ){\r
- obj = { x : alt_context, f : arg2, k : X_Callback_THIS_FUNC };\r
+ obj = { context : alt_context, func : arg2, kind : X_Callback_THIS_FUNC };\r
} else {\r
- obj = { f : arg2, k : X_Callback_FUNC_ONLY };\r
+ obj = { func : arg2, kind : X_Callback_FUNC_ONLY };\r
};\r
} else\r
if( alt_context ){\r
- obj = { x : alt_context, k : X_Callback_HANDLEEVENT };\r
+ obj = { context : alt_context, kind : X_Callback_HANDLEEVENT };\r
arg3 = arg1;\r
} else {\r
console.log( '不正 ' + arg1 );\r
return;\r
};\r
\r
- if( X.Type.isArray( arg3 )){\r
- obj.s = arg3;\r
+ if( X_Type_isArray( arg3 )){\r
+ obj.supplement = arg3;\r
+ };\r
+ return ( obj.context || obj.supplement ) ? obj : arg1;\r
+};\r
+\r
+function X_Callback_actualClosure( obj ){\r
+ return function(){\r
+ if( arguments[ 0 ] === X_Closure_COMMAND_BACK ) return obj;\r
+ if( arguments[ 0 ] !== X_Closure_COMMAND_DROP ) return obj.proxy( obj, arguments );\r
};\r
- return ( obj.x || obj.s ) ? obj : arg1;\r
};\r
\r
function X_Callback_proxyCallback( xfunc, _args ){\r
var args = _args || [],\r
- thisObj = xfunc.x,\r
- func = xfunc.f,\r
- supp = xfunc.s,\r
+ thisObj = xfunc.context,\r
+ func = xfunc.func,\r
+ supp = xfunc.supplement,\r
temp, ret; \r
\r
if( supp && supp.length ){\r
args = temp;\r
};\r
\r
- switch( xfunc.k ){\r
+ switch( xfunc.kind ){\r
\r
case X_Callback_THIS_FUNC :\r
return args.length === 0 ? func.call( thisObj ) : func.apply( thisObj, args );\r
\r
case X_Callback_HANDLEEVENT :\r
temp = thisObj[ 'handleEvent' ];\r
- if( X.Type.isFunction( temp ) ){\r
+ if( X_Type_isFunction( temp ) ){\r
return args.length === 0 ? thisObj[ 'handleEvent' ]() :\r
args.length === 1 ? thisObj[ 'handleEvent' ]( args[ 0 ] ) : temp.apply( thisObj, args );\r
};\r
break;\r
/*\r
- if( temp !== func && X.Type.isFunction( temp ) ){\r
+ if( temp !== func && X_Type_isFunction( temp ) ){\r
return args.length === 0 ? thisObj[ 'handleEvent' ]() : temp.apply( thisObj, args );\r
} else\r
- if( X.Type.isFunction( thisObj ) ){\r
+ if( X_Type_isFunction( thisObj ) ){\r
return args.length === 0 ? thisObj.call( thisObj ) : thisObj.apply( thisObj, args );\r
};\r
return args.length === 0 ? func.call( thisObj ) : func.apply( thisObj, args );*/\r
X_Callback_LIVE_LIST.splice( i, 1 );\r
X_Callback_POOL_LIST[ X_Callback_POOL_LIST.length ] = f;\r
obj = f( X_Closure_COMMAND_BACK );\r
- delete obj.k;\r
- if( obj.f ) delete obj.f;\r
- if( obj.x ) delete obj.x;\r
- if( obj.s ) delete obj.s;\r
+ delete obj.kind;\r
+ if( obj.func ) delete obj.func;\r
+ if( obj.context ) delete obj.context;\r
+ if( obj.supplement ) delete obj.supplement;\r
+ delete obj.proxy;\r
return true;\r
};\r
return false;\r
};\r
\r
-\r
-// sys\r
-X_TEMP.X_Callback_onSystemReady = function( sys ){\r
- delete X_TEMP.X_Callback_onSystemReady;\r
- sys.monitor( X_Callback_monitor );\r
- sys.gc( X_Callback_gc );\r
-};\r
-\r
function X_Callback_monitor(){\r
return {\r
'Callback:Live' : X_Callback_LIVE_LIST.length,\r
X_Callback_POOL_LIST.length = 0; // ?\r
};\r
\r
-X_TEMP.onSystemReady.push( X_TEMP.X_Callback_onSystemReady );\r
+X_TEMP.onSystemReady.push( function( sys ){\r
+ sys.monitor( X_Callback_monitor );\r
+ sys.gc( X_Callback_gc );\r
+});\r
\r
\r
console.log( 'X.Core.Callback' );\r