下午写代码遇到一个奇怪的问题,事件触发后处理函数获取 currentTarget 却为 null。
后来发现,问题出在处理函数是异步访问 currentTarget 上,具体解释前,需要先知道 event 对象中 currentTarget 的“生命周期”。看下面的简单例子:
class Event { constructor(type, target) { this.type = type this.target = target this.currentTarget = null } static fire(type, target, currentTarget) { const event = new Event(type, target) // 事件触发时, 将 currentTarget 绑定到事件对象中 event.currentTarget = currentTarget // 执行监听的函数 event.currentTarget.eventListener[type].forEach(fn => fn(event)) // 事件处理结束后,再将 currentTarget 移除 event.currentTarget = null }}
事实上,currentTarget 只存在于事件触发到事件处理结束之间,随后就重置为 null 了
所以,当我们有下面一段处理函数时
const handler = (e) => { console.log('handling, currentTarget = ', e.currentTarget) // 异步访问 currenTarget, 此时事件处理已结束, currentTarget 被重置为 null setTimeout(() => console.log('timeup,', 'currentTarget = ', e.currentTarget), 0)}
控制台输出结果如下:
// 控制台输出// handling, currentTarget = Target { name: 'currentTarget', eventListener: { click: [ [λ: handler] ] } }// timeup, currentTarget = null
同步的代码能够顺利访问到 currentTarget 而异步代码则为 null
解决的方案很简单,就是把 currentTarget 保存起来,所以处理函数可以这样修改:
const handler = (e) => { console.log('handling, currentTarget = ', e.currentTarget) const currenTarget = e.currenTarget // 异步访问 currenTarget, 此时事件处理已结束, currentTarget 被重置为 null setTimeout(() => console.log('timeup,', 'currentTarget = ', currenTarget), 0)}
问题解决~
完整的测试代码:
class Event { constructor(type, target) { this.type = type this.target = target this.currentTarget = null } static fire(type, target, currentTarget) { const event = new Event(type, target) // 事件触发时, 将 currentTarget 绑定到事件对象中 event.currentTarget = currentTarget // 执行监听的函数 event.currentTarget.eventListener[type].forEach(fn => fn(event)) // 事件处理结束后,再将 currentTarget 移除 event.currentTarget = null }}class Target { constructor(name) { this.name = name this.eventListener = { 'click': [] } } addEventListener(type, fn) { this.eventListener[type] && this.eventListener[type].push(fn) }}const handler = (e) => { console.log('handling, currentTarget = ', e.currentTarget) // 异步访问 currenTarget, 此时事件处理已结束, currentTarget 被重置为 null setTimeout(() => console.log('timeup,', 'currentTarget = ', e.currentTarget), 0)}const currentTarget = new Target('currentTarget')const target = new Target('target')currentTarget.addEventListener('click', handler)Event.fire('click', target, currentTarget)// 控制台输出// handling, currentTarget = Target { name: 'currentTarget', eventListener: { click: [ [λ: handler] ] } }// timeup, currentTarget = null