JavaScript(或jQuery)中的自定义广播事件是如何实现的?

How are custom broadcast events implemented in JavaScript (or jQuery)?

我想实现一个可以 "broadcast" 的自定义事件,而不是发送到特定目标。只有那些已将自己注册为此类事件侦听器的元素才会收到它们。

我的想法如下。

首先,在代码的各个地方,都会有

形式的语句
some_subscriber.on_signal( 'some_signal', some_handler );

我使用术语 signal 作为 shorthand 代表 "broadcast event"。在上面的表达式中,some_subscriber 通过为其提供处理程序将自己注册为此类信号的一种类型(称为 'some_signal')的侦听器。

在代码的其他地方,会有形式为

的语句
publisher.signal_types[ 'some_signal' ].broadcast( event_data );

当执行这些语句时,会生成一个新事件和“广播”。我的意思是调用 broadcast 方法的代码没有关于它发出的信号的侦听器的直接信息。


我在 this jsFiddle 中实现了这个想法的草图,主要是为了说明我在上面的文字中描述的内容 1。 (它肯定不是生产级的,我不是特别有信心它能做到这一点。)

此实施的关键要素如下。首先,发布者对象 不跟踪他们的订阅者 ,从为这样的发布者实现工厂方法可以看出,如下所示:

function make_publisher ( signal_types ) {

    // ...

    var _
    ,   signal = {}
    ,   ping = function ( type ) {
                   signal[ type ].broadcast( ... );
               }
    ;

    signal_types.forEach( function ( type ) {
                             signal[ type ] = $.register_signal_type( type );
                         } );

    return { signal_types: signal_types, ping: ping };
}

此发布者对象仅公开两项:它广播的信号类型(在 signal_types 中)和 ping 方法。当其 ping 方法被调用时,发布者通过 广播 信号响应:

signal[ type ].broadcast( ... )

此广播的最终接收者在此代码中无处可见

其次,在代码的其他地方,订阅者将自己注册为这些广播信号的听众,就像这样

    $( some_selector ).on_signal( signal_type, some_handler );

注意:用一个既小又现实的例子来说明这个方案的原理基本上是不可能的。这样做的原因是这个方案的优势在于它支持发布者代码和订阅者代码之间非常松散的耦合,而这是一个在小例子中永远不需要的特性。相反,在一个小例子中,实现这种松散耦合的代码总是给人留下不必要的复杂印象。因此,重要的是要记住,这种明显的过度复杂性是上下文的产物。松散耦合在大型项目中非常有用。特别是,通过 publisher/subscriber-type 模式实现的松散耦合是 MVC 的基本特征之一。


My question is: is there a better (or at least more standard) way to achieve this effect of "broadcasting" custom events?

(我对基于 jQuery 和 "pure JS" 的答案都感兴趣。)


1这个 post 的一个早期的、不幸的版本遇到了几乎普遍的不理解,而且(当然)所有-太典型的否决票。除了一个例外,我收到的所有评论都挑战了 post 的前提,并且有人直接质疑我对事件驱动编程等基础知识的掌握。我希望通过展示一个工作示例我的意思是至少它不会像我单独用文字描述时那样让人觉得不可思议。幸运的是,我之前在 post 上收到的一条有用的评论告诉我函数 jQuery.Callbacks。这确实是一个有用的提示; post 中提到的草图实现基于 jQuery.Callbacks

My question is: is there a better (or at least more standard) way to achieve this effect of "broadcasting" custom events?

不,Javascript 中没有更标准的 publish/subscribe 方法。它没有直接内置到语言或浏览器中,据我所知,也没有针对它的平台标准。

您有多种选择(其中大部分您似乎都知道)来组合您自己的系统。

您可以选择一个特定对象,例如 document 对象或 window 对象或您创建的新对象并使用 jQuery 的 .on().trigger() 将该对象作为中央票据交换所,拼凑出一个 publish/subscribe 样的模型。如果需要,您甚至可以通过将其编码为一些实用函数来隐藏该对象的存在,使其不被实际使用。

或者,您似乎已经知道,您可以使用 jQuery.Callbacks 功能。 jQuery doc.

中甚至还有 publish/subscribe 示例代码

或者,您可以找到提供有点传统 publish/subscribe 模型的第三方库。

或者,您可以从头开始构建自己的回调函数,这实际上只涉及保留与特定事件关联的回调函数列表,以便在触发该事件时调用每个回调函数。

好的。

所以我认为你可以做的是使用本机 dispatchEventaddEventListener 方法并使用document 作为发布订阅 的唯一元素。类似于:

var myCustomEvent = new Event('someEvent');
document.dispatchEvent(myCustomEvent);
...
document.addEventListener('someEvent', doSomething, false);

要实现跨浏览器,您可以:

var myCustomEvent = new Event('someEvent');
document.dispatchEvent(myCustomEvent);
...
if (document.addEventListener) {
    document.addEventListener('someEvent', doSomething, false);
} else {
    document.attachEvent('someEvent', doSomething);
}

您可以阅读有关该主题的更多信息 here and here。希望这有帮助。

如果您是来这里寻找 jQuery 方法的,请看这里:

添加活动broadcast/dispatch代码:

语法:
$(<element-name>).trigger(<event-name>);

示例:

$.ajax({
    ...
    complete: function () {
        // signal to registered listeners that event has occured
        $(document).trigger("build_complete");
        ...
    }
});

为事件注册监听器:

语法:
$(<element-name>).on(<event-name>, function() {...});

示例:

$(document).on("build_complete", function () {
    NextTask.Init();
});

注意: 这样做:$(document).build_complete(function() {...}); 会导致错误:Uncaught TypeError: $(...).build_complete is not a function.

我知道这已在 2015 年被标记为已回答——但一个优雅而简单的解决方案可能是使用 Redux