事件触发 currentTarget 却为 null

935 阅读2分钟

下午写代码遇到一个奇怪的问题,事件触发后处理函数获取 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