在保持绑定范围

时间:2020-10-23 07:33:43

标签: javascript typescript dom-events

我正在为addEventListener创建一个小的帮助程序类。我遇到的问题是,我无法同时获得类的this上下文和Event的this上下文(附加的事件目标)。

(1)如果使用箭头函数,则可以获取该类的this,但是无法获取(通过Function.prototype.call)绑定的this

(2)如果使用函数表达式,则可以对此进行限制,但无法访问该类。

(3)我也不能使用内部闭包。该功能/方法必须从外部范围引用。

这是一个简化的示例,向您展示我的意思。有没有办法勾选所有框?我能想到的就是创建另一个助手类,该助手类将为连接的每个事件侦听器初始化,但是如果有更简单的方法,这似乎效率不高。

class EventListenerHelper {
    private targets: Array<EventTarget>
    constructor() {
        // If there was a single target, it would be very easy. But there are multiple
        this.targets = [
            document.body,
            window
        ];
    }

     /**
     * (1) - Try to use an arrow function
     * 
     * This falls short because it's not possible to get the this context of the Event
     */
    private attachWithArrowFunction() {
        this.targets.forEach((target) => {
            target.addEventListener('click', this.listenerCallbackArrow, false);
        });
    }

    private listenerCallbackArrow = (e: Event) => {
        // Cannot get event this
        const eventThis = undefined; 

        // Note that e.target is the innermost element which got hit with the event
        // We are looking for that target that had the event listener attached
        // If I'm not mistaken, the only way to get it is from the this context
        // which is bound to the event callback

        this.processListener(eventThis, e);   
    }

    /**
     * (2) - Try to use a regular class method
     * 
     * This falls short because it's not possible to get the this context of the class
     */
    private attachWithClassMethod() {
        this.targets.forEach((target) => {
            target.addEventListener('click', this.listenerCallbackMethod, false);
        });
    }

    private listenerCallbackMethod(e: Event) {
         // Here we have the eventThis 
        const eventThis = this;

        // But the class instance is completely unreachable
    }

    /**
     * (3) - Try to create a closure wrapper
     * 
     * This almost works, but it's not possible to removeEventListener later
     */
    private attachWithClosure() {
        let self = this;

        this.targets.forEach((target) => {
            target.addEventListener('click', function(e: Event) {
                self.processListener(this as EventTarget, e);
            }, false);
        });
    }


    private processListener(eventThis: EventTarget, e: Event) {
        // Do some stuff with other class methods

        // 

    }

    private detach() {
        this.targets.forEach((target) => {
            target.addEventListener('click', this.listenerCallbackArrow, false);
        });
    }

}

1 个答案:

答案 0 :(得分:2)

一种常见的处理方法是返回一个函数:

private attach() {
  const listener = this.getListener()
  this.targets.forEach(target => {
    target.addEventListener('click', listener, false)
  })
}

private getListener() {
  const self = this
  return function (e: Event) {
    // self if EventListenerHelper this
    // this is Event this
  }
}

但是我看不到有什么好处,因为传递给this的函数中的addEventListener等于event.currentTarget,因此您可以绑定监听器并使用该属性而不是this

constructor() {
  // ...
  this.listener = this.listener.bind(this)
}
private listener(e) {
  // this is EventListenerHelper this
  // e.currentTarget is Event this
}