如前文《在类对象中如何正确处理事件回调的 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 个实例变量来分别保存这些事件处理对应的内存地址 (引用)..... 这是多么一种丑陋的设计模式, 就如下面一样的.
但暂时将写法丑陋与否放在一边, 先看看运行结果
正如测试的那样, 只有 button1 的 black_friday() 方法并没有执行
从上面的演示来看这种方法除了不优雅之外, 是能够达到预期的目的的.
以下是本文的主题, 我们解决之前丑陋的问题, 我们可以将每个需要绑定的实例方法和其保存其关于 event=>this.handler(event) 或 this.handler.bind(this) 这两种模式的实例变量, 这里统称 "附加的函数的引用" 的变量, 我们将这些工作交给一个叫 EventManager 的类来自动完成, 好, 那直接上代码吧...
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.
watchEvent 是一个钩子方法
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_register 的实现细节
event_registerOnce(elem, types, handler)
这是一个 event_register 的变种, 事件仅注册一次, 执行一次后即删除, 需要在事件处理器中调用 watchEvent 方法配合使用
elem: 目标 DOM
types: 事件类型
handler: 事件处理器函数
event_registerOnce 的实现细节
event_entry(name)
一个用于迭代并且获取对应事件处理器的 "附加的函数的引用" 的对应条目, 需要传入事件处理的方法名称, 目前仅用作调试只用, 实际项目开发中然并卵...
获取指定事件处理方法的实现
event_revoke(elem, types, name)
* 事件处理函数注销, 这是实际执行 removeEventHandler 操作的封装方法. 由钩子方法 watchEvent 执行调用.
elem: 目标 dom
types: 事件类型
name: 事件处理器的方法名称
这是一个 JavaScript 原生的实现
现在对 MyTest 类继承并且使用 Event 类的方法, 如下图
black_friday 调用了 watchEvent 表示其方法在 click 事件中只执行一次
init_event_handler() 中是简化后代码的最终效果, yeah~~ 效果不错吧!, 下图是调试模式下, 可以查看事件队列的执行情况.
调试模式效果
来源: http://www.jianshu.com/p/06c01e24a0fc