下午写代码遇到一个奇怪的问题,事件触发后处理函数获取 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'} }
// timeup, currentTarget = null
同步的代码能够顺利访问到 currentTarget 而异步代码则为 null
解决的方案很简单,就是把 currentTarget 保存起来,所以处理函数可以这样修改:
const handler = (e) => {
const currenTarget = e.currenTarget
// 异步访问 currenTarget, 此时事件处理已结束, currentTarget 被重置为 null
setTimeout(() => console.log('timeup,', 'currentTarget = ', currenTarget), 0)
}
问题解决~
总结:当事件处理函数中存在异步操作时,需要将 currentTarget 保存以便异步操作顺利访问
完整的测试代码:
const { ENETUNREACH } = require("constants")
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