【问题标题】:Remove "wrapped" event handler for use in pub/sub messaging删除“包装”事件处理程序以用于发布/订阅消息
【发布时间】:2014-10-12 11:11:40
【问题描述】:

我已经使用 jQuery 事件完成了一些发布/订阅。

基本功能有效:

var publish = function(name) {                        // --- works
  var args = Array.prototype.slice.call(arguments);   // convert to array
  args = args.slice(1);                               // remove arg1 (name)
  $("html").trigger(name, args);
}

var subscribe = function(name, callback) {            // --- works
  $("html").on(name, callback);
}

var unsubscribe = function(name, callback) {          // --- works
  $("html").off(name, callback);
}

// usage:                                             // --- works
var myhandler = function () { /* ... */ };
subscribe("foo", myhandler);
publish("foo", 1, false, "bob", {a:1, b:"2"});
unsubscribe("foo", myhandler);

但是以我想要的方式订阅,却没有:

var subscribe = function(name, callback) {            // --- can't unsub this
  $("html").on(name, function () {
    var args = Array.prototype.slice.call(arguments);
    args = args.slice(1);
    callback.apply(null, args);
  });
}

我想要第二个 subscribe 函数,因为匿名回调会删除我不希望在“干净”客户端代码中出现的第一个参数(Event 对象)。我只想在回调中接收到事件数据,我不想要 Event 对象本身。

但这意味着我不能成功调用unsubscribe,因为我没有传递给off() 与传递给on() 相同的回调——这不是实际的回调,而是一个包装的匿名函数。

有没有办法解决这个问题?

【问题讨论】:

    标签: javascript jquery publish-subscribe jquery-events


    【解决方案1】:

    通常的方式是使用jQuery的"namespaced" events:

    $("html").on(name + ".my-pub-sub", function () {
        // ...
    });
    

    然后退订:

    $("html").off(name + ".my-pub-sub");
    

    这告诉 jQuery 删除所有具有给定事件名称和特定命名空间的处理程序。

    这是一个简化的例子:

    // Add two click handlers:
    $("button").on("click", function() {
      $("<p>Non-namespaced click</p>").appendTo(document.body);
    });
    $("button").on("click.namespace", function() {
      $("<p>Namespaced click</p>").appendTo(document.body);
    });
    
    // Show they both trigger
    $("<p>Clicking...</p>").appendTo(document.body);
    $("button").click();
    
    // Remove the namespaced one without a function ref
    $("button").off("click.namespace");
    
    // Now just the non-namespaced one runs
    $("<p>Clicking again...</p>").appendTo(document.body);
    $("button").click();
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <button>The button</button>

    【讨论】:

    • 是的,但是如果我有多个订阅者该事件怎么办。如果我使用off() 和命名空间,它会删除所有,而不仅仅是我想要的?
    • @hbob:您可以为每个订阅者分配不同的命名空间。最重要的是,取消订阅时,您需要 something 来标识要删除的内容(您要附加的实际功能或特定的命名空间)。
    • @TJCrowder:是的,这是有道理的。我已经添加了我的解决方案作为答案,你怎么看?我接受了你的想法,因为它给了我最初的想法。
    【解决方案2】:

    您可以像这样返回新创建的事件处理程序:

    var subscribe = function(name, callback) {
      var handler = function () {
        var args = Array.prototype.slice.call(arguments);
        args = args.slice(1);
        callback.apply(null, args);
      };
      $("html").on(name, handler);
      return handler;
    };
    

    现在你可以像这样使用它了:

    var myhandler = function () { console.log(arguments); };
    var eventHandler = subscribe("foo", myhandler);
    publish("foo", 1, false, "bob", {a:1, b:"2"}); // logs to console
    unsubscribe("foo", eventHandler);
    publish("foo", 1, false, "bob", {a:1, b:"2"}); // don't log anymore
    

    这里是demo

    【讨论】:

    • 原来这与@TJCrowder 提议的相同。效果很好!
    【解决方案3】:

    我所做的是基于@TJCrowder 提出的命名空间理念。但我已将他的答案标记为谢谢。

    var subscribe = function(name, callback) {
    
      // create a unique "key" for this specific event handler
      // - use this format: ORIGINALNAME.MILLISECONDS.RANDOMNUMBER
      // - include time in random string, as could be lots of subs happening at once
      // - then include a random number, just to be safe
      var key = name + "." + (new Date().getTime()) + (Math.random()*1000);
    
      $("html").on(key, function () {
        var args = Array.prototype.slice.call(arguments);
        args = args.slice(1);
        callback.apply(null, args);
      });
    
      return key;
    }
    
    var unsubscribe = function (key) {
      $("html").off(key);
    };
    
    // usage:
    var myhandler = function () { /* ... */ };
    var key = subscribe("foo", myhandler);
    publish("foo", 1, false, "bob", {a:1, b:"2"});
    unsubscribe(key);
    

    由于这个键是唯一的(嗯,它应该是?!),它只会删除 这个特定的 处理程序。所以我可以有很多订阅者,并且只能取消订阅其中一个。

    【讨论】:

    • 看起来不错。 (除了:Math.random()*(1000-0)+0 是什么?这与Math.random()*1000 完全相同。)当然,如果您要记住一些独特的东西,您可以只返回对您正在创建的函数的引用。您想在取消订阅时释放引用,这很容易做到:key = unsubscribe(key); 由于unsubscribe 不返回任何内容,调用它会导致undefined,它被分配给key,因此该函数不再有任何对它的引用并且可以被垃圾收集。
    • @TJCrowder 虽然你是 100% 正确的,而且你的方法更简单,但我想我更喜欢将唯一的字符串作为键返回。使忘记强制垃圾收集器的开发人员使用该功能时更不容易发生内存泄漏。
    • 对。该函数仅存在一份副本,返回的值将是对该函数的引用。
    • “使忘记强制垃圾收集器的开发人员在使用该函数时不易发生内存泄漏” 是和否。如果他们不清除key,那么这个字符串就在附近。但是字符串可能比函数小。不是很多(对于您展示的功能),但仍然...
    • 想一想:不仅仅是函数,还有函数关闭的上下文,以及这些上下文引用的任何内容,所以虽然字符串可能不会比函数小很多,但我敢打赌它是一个比函数保存在 RAM 中的所有内容要小得多。好电话。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-15
    • 1970-01-01
    • 2021-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多