0

以下の例は、カスタムJavaScriptオブジェクトでイベント処理を提供するための2つの一般的な最新のアプローチを示しています。(私は思います。)この例は、AJAXを介してjQueryにデータをロードする一般的なシナリオに基づいています。このシナリオでは、パラメーターが渡され、公開される最終的に完了したイベントでも提供する必要があります。

CustomClass.prototype.loadData(param1) {

  var thisObj = this;
  $.ajax({
    url : this.reqUrl,
    context: this,

    // There are various ways to do this, including writing the handlers inline
    // or using $.proxy. Shouldn't be important for this question.
    success : function(dataResp, textStatus) {thisObj.onSuccess(dataResp,
                                                                textStatus,
                                                                param1)}
  });
};

// Manage listeners. Storing functions here, but
// could also use objects with predefined functions.
CustomClass.prototype.addListener(callbackFunc) {
  this.listeners.Add(callbackFunc);
}

CustomClass.prototype.onSuccess(dataResp, textStatus, param1) {

  //**OPTION 1**
  // Publish event via callbacks/listeners
  for(var i=0; i<this.listeners; i++)
    this.listeners[i](dataResp, param1);

  //**OPTION 2**
  // Publish event via trigger/on
  $(this).trigger('my-event-name', [dataResp, param1]);

};

以下は、これら2つのオプションがコードでどのように見えるかを示しています。

function handler(dataResp, param1) {
  // do work, param1 == 'whatever'
}

c1 = new CustomClass();
c1.addListener(handler);
c1.loadData('whatever');

c2 = new CustomClass();
$(c2).on('my-event-name', handler);
c2.loadData('whatever');

結果は、上記のどちらのオプションでも同じです。jqueryのon/triggerサポートを使用すると、リスナーを管理するためのコードが不要になりますが、次のようになります。

  • 一方のアプローチはもう一方のアプローチよりも間違いなく優れていますか?
  • オン/トリガーを使用する場合、パフォーマンスなどの重要なトレードオフはありますか?

DOM要素でイベントリスナースタイルのプログラミングを使用する利点に関して、SOには多くの質問/回答があります。また、非DOMオブジェクトでイベント処理を提供するさまざまな方法に関する多くの質問/回答があります。ただし、特に上記の例のような重要なユースケースでは、非DOMオブジェクトの 推奨されるアプローチと潜在的なトレードオフに関して決定的なものは見つかりませんでした。

編集...もちろん、投稿した後、この質問/回答が(非常に)近くなることがわかります。ただし、カスタムオブジェクトにプロパティを追加する以外に、他の影響があるかどうかは示されません。また、私の質問はより明確なユースケースを提供すると思います。

4

1 に答える 1

0

私自身の質問への答えとして:

DOM インタラクションの一部としてイベントを生成または消費しないカスタム オブジェクトを作成する場合は、コールバックを使用することをお勧めします。jQuery の on/trigger イベント処理を使用すると、jQuery のより大きなイベント処理サブシステムにフックされるため、多くの余分なコード パスが継承されます。ロジックの一部は、ブラウザーの違いを処理するためのものです。追加のコードが使用されるのは、Web ブラウザーに見られるイベント処理の機能を再利用および/または複製するためですが、クロスブラウザーのコンテキストで行われます。

対照的に、カスタム コールバック処理の使用は簡単です。Jquery は、コーディングを節約できる $.Callbacks 関数も提供し、オプションでより高度なコールバック機能を含めることができます。

この質問に対するより完全な回答は、さまざまなアプローチのタイミング分析を提供します。ただし、jquery v1.9.1 のソースだけでも、強力な逸話的証拠が得られます。

jQuery のトリガー メソッド:

trigger: function( event, data, elem, onlyHandlers ) {
    var handle, ontype, cur,
        bubbleType, special, tmp, i,
        eventPath = [ elem || document ],
        type = core_hasOwn.call( event, "type" ) ? event.type : event,
        namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];

    cur = tmp = elem = elem || document;

    // Don't do events on text and comment nodes
    if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
        return;
    }

    // focus/blur morphs to focusin/out; ensure we're not firing them right now
    if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
        return;
    }

    if ( type.indexOf(".") >= 0 ) {
        // Namespaced trigger; create a regexp to match event type in handle()
        namespaces = type.split(".");
        type = namespaces.shift();
        namespaces.sort();
    }
    ontype = type.indexOf(":") < 0 && "on" + type;

    // Caller can pass in a jQuery.Event object, Object, or just an event type string
    event = event[ jQuery.expando ] ?
        event :
        new jQuery.Event( type, typeof event === "object" && event );

    event.isTrigger = true;
    event.namespace = namespaces.join(".");
    event.namespace_re = event.namespace ?
        new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
        null;

    // Clean up the event in case it is being reused
    event.result = undefined;
    if ( !event.target ) {
        event.target = elem;
    }

    // Clone any incoming data and prepend the event, creating the handler arg list
    data = data == null ?
        [ event ] :
        jQuery.makeArray( data, [ event ] );

    // Allow special events to draw outside the lines
    special = jQuery.event.special[ type ] || {};
    if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
        return;
    }

    // Determine event propagation path in advance, per W3C events spec (#9951)
    // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
    if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {

        bubbleType = special.delegateType || type;
        if ( !rfocusMorph.test( bubbleType + type ) ) {
            cur = cur.parentNode;
        }
        for ( ; cur; cur = cur.parentNode ) {
            eventPath.push( cur );
            tmp = cur;
        }

        // Only add window if we got to document (e.g., not plain obj or detached DOM)
        if ( tmp === (elem.ownerDocument || document) ) {
            eventPath.push( tmp.defaultView || tmp.parentWindow || window );
        }
    }

    // Fire handlers on the event path
    i = 0;
    while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {

        event.type = i > 1 ?
            bubbleType :
            special.bindType || type;

        // jQuery handler
        handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
        if ( handle ) {
            handle.apply( cur, data );
        }

        // Native handler
        handle = ontype && cur[ ontype ];
        if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
            event.preventDefault();
        }
    }
    event.type = type;

    // If nobody prevented the default action, do it now
    if ( !onlyHandlers && !event.isDefaultPrevented() ) {

        if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
            !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {

            // Call a native DOM method on the target with the same name name as the event.
            // Can't use an .isFunction() check here because IE6/7 fails that test.
            // Don't do default actions on window, that's where global variables be (#6170)
            if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {

                // Don't re-trigger an onFOO event when we call its FOO() method
                tmp = elem[ ontype ];

                if ( tmp ) {
                    elem[ ontype ] = null;
                }

                // Prevent re-triggering of the same event, since we already bubbled it above
                jQuery.event.triggered = type;
                try {
                    elem[ type ]();
                } catch ( e ) {
                    // IE<9 dies on focus/blur to hidden element (#1486,#12518)
                    // only reproducible on winXP IE8 native, not IE9 in IE8 mode
                }
                jQuery.event.triggered = undefined;

                if ( tmp ) {
                    elem[ ontype ] = tmp;
                }
            }
        }
    }

    return event.result;
}

追加情報については、@kevin-b によるこの質問へのコメントと、この古い質問/回答を参照してください。

于 2013-03-04T14:09:34.207 に答える