5

我一直在互联网上寻找在 jquery 中实现观察者模式的示例。

我想要这样

observer1.observe(subject);
observer2.observe(subject);

为观察者定义一些自定义事件回调

observer1.bind('customEvent', function(contextData) {
  //Some code
});
observer1.bind('anotherCustomEvent', function(contextData) {
  //Some code  
});
observer2.bind('customEvent', function(contextData) {
  //Some code  
});

然后,以下行将触发两个观察者的 customEvent 回调

subject.trigger('customEvent', contextData);

而以下只会在observer1上触发anotherCustomEvent,因为observer2没有绑定该自定义事件

subject.trigger('anotherCustomEvent', contextData);

互联网上的指南更笼统:

$( document ).on( "topicName" , function () {
  //..perform some behaviour
});

$( document ).trigger( "topicName" );

(来自http://addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjquery的示例)我看不出如何使用上面的代码来完成我正在寻找的东西。

要么我必须这样做(如果我像上面的例子一样保持它):

$(document).on("customEvent", function () {
  observer1.trigger("customEvent");
  observer2.trigger("customEvent");
});

$(subject).click(function() { 
  $(document).trigger("customEvent");
});

或者更好一点:

$(subject).click(function() { 
  observer1.trigger("customEvent");
  observer2.trigger("customEvent");
});

无论哪种方式,我都不得不编辑主题点击回调或文档自定义事件回调,而不是告诉观察者订阅主题。

我是否误解了观察者模式,或者有没有办法实现我正在寻找的东西?

http://addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjavascript在那一章的后面稍微提到了发布/订阅模式。这对我来说可能是一种方式,但我错过了示例背后的代码。

4

3 回答 3

17

从您的评论中:

当您必须告诉主题要由选择器触发哪些元素而不是主题具有观察者可以注册到的列表时,我只是看不到这是如何实现的

如果我错了,请纠正我,但您似乎误解了该模式是如何在 jQuery 中实现的。您不会“告诉主题要触发哪些元素”,主题也没有“观察者可以注册的列表”。它是这样工作的:

  • 主题/发布者发出/触发某些事件(在您定义的某些情况下)。
  • 观察者/订阅者监听某些事件。它保留了它订阅的事件的列表。
  • 这都是基于 DOM 事件的,所以受到 DOM 事件模型的限制。

例如,考虑以下 HTML:

<div id="div1">div 1</div>
<div id="div2">div 2</div>

让我们让内部 div 触发一个名为'custom'. 您定义什么时候应该发生,在这个例子中,这将在单击它们时发生:

$('div').on('click', function(e) {
    $(this).trigger('custom');
});

现在让我们让document元素订阅该自定义事件:

$(document).on('custom', function(e) {
    console.log('document is handling custom event triggered by ' + e.target.id);
});

当自定义事件由其中一个 div 触发时,会通知观察者/订阅者并将一条消息记录到控制台。

该示例document用作观察者是有原因的:事件在 DOM 树中冒泡,并且只能被触发它的元素的祖先元素捕获。由于document是 DOM 树的根,它可以看到所有事件。如果#div1是我们的观察者,它只会看到由#div1自己触发的事件,而不会看到由#div2.

也许这个限制让你感到困惑?


有一些方法可以规避这个限制,但通常,如果你想#div1根据由 触发的事件来做某事#div2,你只需从你在document元素上设置的回调(或两个 div 最接近的共同祖先)中进行操作。无论如何,您似乎真的想要一个替代方案,所以这里有一个 jQuery 插件形式的替代方案:

$.fn.observe = function(eventName, callback) {
    return this.each(function(){
        var el = this;
        $(document).on(eventName, function(){
            callback.apply(el, arguments);
        })
    });
}

你可以像这样使用它:

$('#div1').observe('custom', function(e) {
    // do something
});

现场示例:http: //jsfiddle.net/JwJsP/1

于 2012-09-26T13:29:14.370 回答
0

可能不是您想要的,但是观察者可以订阅监听事件:

        $(document).on('customEvent', '.iObserver', function() {
            console.log('i am observer');
        });

        // add observer
        $('ul li#Observer').addClass('iObserver');

        //remove observer
        $('ul li#Observer').removeClass('iObserver');
于 2012-09-25T22:02:12.533 回答
-1

如果您仍在寻找使用 jQuery 的事件引擎的一些实现,您可以尝试以下代码:

//The implementation   
(function($) {

    var o = $({});

    $.each({
        trigger: 'publish',
        on: 'subscribe',
        off: 'unsubscribe'
    }, function(key, val) {
        jQuery[val] = function() {
            o[key].apply(o, arguments);
        };
    });

})(jQuery);


// The way to use it is this:
var somedata = [1, 2, 3, 5, 4, 7, 8];
$.publish('myEvent', somedata); // with data
$.publish('myEvent'); // without data

$.subscribe('myEvent', function(event, data) {
    // handle the event
});
于 2015-02-18T15:09:39.433 回答