【问题标题】:Javascript Module Pattern Events and ListenersJavascript 模块模式事件和监听器
【发布时间】:2011-11-11 20:48:02
【问题描述】:

我正在实现模块模式,并且想知道定义和注册事件侦听器/处理程序的最佳/首选方式。以下工作,但也许有更好/更简单的方法......

var  MODULE = function() {

    //  private
    var _field1;
    var _field2;

    function  localFunc(p) {
        alert('localFunc');
    }

    //  public
    return {
        // properties
        prop1: _field1,

        // events
        myEvent1Handler: {},
        myEvent1: function() {myEvent1Handler();},
        myEvent2Handler: {},
        myEvent2: function() {myEvent2Handler();},

        addListener: function  (event,func) {
            if (event  ==  "myEvent1")
                myEvent1Handler = func;   

            if (event  ==  "myEvent2")
                myEvent2Handler = func;      
        },

        // public  methods
        method1: function (p) {
            alert('method1 says:' + p);
            MODULE.myEvent1();
        },
        method2: function  (p) {
             alert('method2 doing  stuff');
             localFunc(p);
            MODULE.myEvent2();
        }

    };
}();

// register for events
MODULE.addListener("myEvent1",function(){alert('fired1');});  
MODULE.addListener("myEvent2",function(){alert('fired2');});  

// use module (only event1 should fire!)
MODULE.method1("hello");  

试试看:

http://jsfiddle.net/RqusH/3/

拥有 myEventx、myEventHandlerx 和 addListener 似乎需要做很多工作?

【问题讨论】:

  • 当你调用addListener()时,不就是创建myEvent1HandlermyEvent2Handler作为global变量引用传入的func参数吗?它不会设置已在对象中定义的同名属性,除非您在 myEvent1 函数中说 this.myEvent1Handler = ... - 然后是 this.myEvent1Handler() 。 (或者MODULE.myEvent1Handler 就像你在method1 中拥有MODULE.myEvent1() 一样。)
  • 你真正想解决什么问题?这似乎是很多开销,实际上除了搭建脚手架之外什么也没做。或许如果我们知道您真正想要完成的工作,我们可以帮助您找到最好的方法。
  • 谢谢...我想在 MODULE 中进行事件,以便 MODULE 的用户可以挂钩事件。 MODULE 方法将有异步请求(使用 jquery),所以我想使用事件来指示异步请求何时完成。

标签: javascript module-pattern


【解决方案1】:

通常,我不会使用特定实现(尤其是依赖于 jQuery 等第三方库的架构问题)来回答架构问题,但由于我的实现促进了可重用性——我们在这里讨论的是模式——我将展示一个小型 jQuery 插件,$.eventable,它使用 jQuery 的事件方法来扩充 JavaScript 对象。

这个插件将允许您通过一个简单的调用在任何对象(或类实例)上实现事件处理能力。

jQuery.eventable = function (obj) {
  // Allow use of Function.prototype for shorthanding the augmentation of classes
  obj = jQuery.isFunction(obj) ? obj.prototype : obj;
  // Augment the object (or prototype) with eventable methods
  return $.extend(obj, jQuery.eventable.prototype);
};

jQuery.eventable.prototype = {

  // The trigger event must be augmented separately because it requires a
  // new Event to prevent unexpected triggering of a method (and possibly
  // infinite recursion) when the event type matches the method name
  trigger: function (type, data) {
    var event = new jQuery.Event(type); 
    event.preventDefault();                
    jQuery.event.trigger(event, data, this);
    return this;
  }
};

// Augment the object with jQuery's event methods
jQuery.each(['bind', 'one', 'unbind', 'on', 'off'], function (i, method) {
  jQuery.eventable.prototype[method] = function (type, data, fn) {
    jQuery(this)[method](type, data, fn);
    return this;
  };
});

如果您包含该 sn-p,您可以像这样实现您的解决方案:

var MODULE = function() {

  //  private
  var _field1;
  var _field2;

  function localFunc(p) {
    alert('localFunc');
  }

  //  public
  return $.eventable({

    // properties
    prop1: _field1,

    // public  methods
    method1: function(p) {
      alert('method1 says:' + p);
      this.trigger('myEvent1');
    },

    method2: function(p) {
      alert('method2 doing  stuff');
      localFunc(p);
      this.trigger('myEvent2');
    }

  });
} ();

// register for events
MODULE.on("myEvent1", function() {
  alert('fired1');
});
MODULE.on("myEvent2", function() {
  alert('fired2');
});

// use module (only event1 should fire!)
MODULE.method1("hello");

您的 MODULE 现在具有以下可调用方法:

MODULE.on(event, /* data, */ handler);
MODULE.bind(event, /* data, */ handler);
MODULE.one(event, /* data ,*/ handler);
MODULE.off(event, handler);
MODULE.unbind(event, handler);
MODULE.trigger(event /*, data */);

其中 event 是以空格分隔的事件列表,handler 是您的回调,data 是传递给您的回调的可选值。

您可以参考jQuery's documentation了解更多详情。

【讨论】:

  • 这非常出色,与我倾向于使用的模式完美匹配。谢谢!我会非常小心地解决这个问题……
【解决方案2】:

显示模块模式让您返回一个匿名对象。如果你改为将它声明为一个变量并返回它,这意味着你可以很容易地使用标准的 jQuery 事件方法,无论是在模块内部引发事件还是在外部响应它们。请注意,您必须事先将其转换为 jQuery 对象。

var controller = (function()
{
  function doAThing()
  {
    // Things getting done here
    $(interface).trigger("complete");
  }

  var interface = {
    doAThing: doAThing
  };
  return interface;
})();

$(controller).on("complete", function() { console.log("Thing Done"); });
controller.doAThing();

【讨论】:

  • 尽量不要使用变量名接口 thou,留作以后使用。
猜你喜欢
  • 1970-01-01
  • 2022-08-20
  • 2019-11-06
  • 1970-01-01
  • 2020-06-21
  • 1970-01-01
  • 2014-07-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多