如前文《在类对象中如何正确处理事件回调的this问题》再续,没看前文的看官,可以看我之前写的随笔。
1.使用箭头函数将类实例的this传递给回调函数
2.使用回调函数原有的bind方法绑定实例this
但请注意,这两种解决方案都会破坏了init_event_handlers上下文与其之后需要执行某些操作的逻辑关系,就拿前面其中一条代码来分析,当你想在程序的其他地方解除对button1和black_friday方法的绑定,
this.button1.removeEventListener('click',(event)=>this.black_friday(event));
那么问题就来,在后续的removeEventListener操作,因为没有保存任何对您“附加的函数的引用”,因此您无法删除事件处理程序。为了验证我的说法,你可以试试如下此时代码
remove_event_handlers()方法是执行了,但button1对应的事件处理black_friday并没有如你所愿地从javascript内置的click事件队列里面删除,因为我下图所示black_friday还执行了12次..囧...
其实解决这个方法就是addEventListener之前为每个按钮的要绑定的事件处理器都一个“实例的变量”,这个实例变量保存着该事件处理的引用,这句话理解很重要....!!也就是说,如果我一个按钮要绑定n个事件处理器(这里是类方法),就要手动创建n个实例变量来分别保存这些事件处理对应的内存地址(引用).....这是多么一种丑陋的设计模式,就如下面一样的。
但暂时将写法丑陋与否放在一边,先看看运行结果
从上面的演示来看这种方法除了不优雅之外,是能够达到预期的目的的。
以下是本文的主题,我们解决之前丑陋的问题,我们可以将每个需要绑定的实例方法和其保存其关于event=>this.handler(event)或this.handler.bind(this)这两种模式的实例变量,这里统称“附加的函数的引用”的变量,我们将这些工作交给一个叫EventManager的类来自动完成,好,那直接上代码吧...
EVENTLIST:
是一个存放每个实例方法进行addEventListener操作后的“附加的函数的引用”的事件队列。
RUNTIME:
表识事件处理器的生命周期,这里表示值得浏览器窗口被关闭。
RUNONCE:
表识事件处理被触发一次后,马上被从事件队列中剔除并且执行removeEventListener操作,这个操作适合某些特殊场合的应用,例如用户第一次在你的app上进行用户注册会有一个点击“同意”某某app条款的操作,并且仅允许用户只点击一次.
watchEvent(name):
是一个钩子函数类型的set修改器,如果你注册的事件处理正如常量RUNONCE描述的那样,在该回调函数执行结束之前调用watchEvent(name),用于从事件队列找到该函数对应的“附加的函数的引用”,并由watchEvent执行removeEventListener操作。以下是关于watchEvent(name)的具体实现。
通过参数提供类方法的名称,通常使用类似“this.handler.name”传入watchEvent,然后迭代EVENTLIST找到对应“附加的函数的引用”的条目,并且查看满足used属性是否>=1后对该事件处理回调执行removeEventListener.
event_register(elem, types, handler,way = EventManager.RUNTIME, isAdd = true)
elem :表示需要绑定的dom元素
types 事件类型
handler 类的实例方法名称指向的引用
way 事件的运行方式,默认为RUNTIME
isAdd 是否添加到事件队列,如果出于调试模式,这项设为true,不考虑RUNONCE所说的特殊应用,请将其设为false,这样可以减少每次执行事件处理器时迭代EVENTLIST事件队列的时间成本。
以下给出的是原生javascript的版本,
如果要跟jquery一起使用可以使用elem.on(type,callback)那条语句.
event_registerOnce(elem, types, handler)
这是一个event_register的变种,事件仅注册一次,执行一次后即删除,需要在事件处理器中调用watchEvent方法配合使用
elem: 目标DOM
types: 事件类型
handler: 事件处理器函数
event_entry(name)
一个用于迭代并且获取对应事件处理器的“附加的函数的引用”的对应条目,需要传入事件处理的方法名称,目前仅用作调试只用,实际项目开发中然并卵...
event_revoke(elem, types, name)
*事件处理函数注销,这是实际执行removeEventHandler操作的封装方法.由钩子方法watchEvent执行调用.elem:目标dom
types:事件类型
name:事件处理器的方法名称
现在对MyTest类继承并且使用Event类的方法,如下图
init_event_handler()中是简化后代码的最终效果,yeah~~效果不错吧!,下图是调试模式下,可以查看事件队列的执行情况.