如何在一个类中删除一个事件监听器?

时间:2019-05-10 14:41:09

标签: javascript

说我有以下代码:

class Sample {
    constructor() {
        this.value = 'something';
    }

    addClickListener(e) { // e is a dom element

        let func = function(e) {
             //what I want to be able to do:
             this.doThing(e);
    }

        e.addEventListener('click', func, false);
    }

    removeClickListener(e) {

        let func = function(e) {
            //what I want to be able to do:
             this.doThing(e);
        }

        e.removeEventListener('click', func, false);
    }

    doThing(p) { 
    //code
    }

}

我希望能够从'func'中引用一个类方法并将其传递一个参数。引用该类(例如让this = self)无效,因为每次调用该函数并更改事件侦听器的签名时,都会对此类进行新版本的引用。

1 个答案:

答案 0 :(得分:2)

要删除事件监听器,您需要保留对要删除的函数的引用:

class Sample {
    constructor() {
        this.value = 'something';
        // I've changed it from "let func" to "this.func" so we retain a reference
        // I also moved it from addClickListener to the constructor so that we
        // don't overwrite our reference when binding more elements to the
        // same function
        this.func = function(e) {
             //what I want to be able to do:
             this.doThing(e); // NOTE: You have another bug here. See comments
        }
    }

    addClickListener(e) { // e is a dom element
        // Replaced "func" with "this.func"
        e.addEventListener('click', this.func, false);
    }

    removeClickListener(e) {

        // Removed the new (unused) function

        // Replaced "func" with "this.func"
        e.removeEventListener('click', this.func, false);
    }

    doThing(p) { 
        //code
    }

}

在我的评论中,我说:“注意:您在这里还有另一个错误”

调用事件侦听器时,上下文(this变量)将更改为发出事件的元素。因此,this.doThing将尝试在元素doThing上调用e

还要注意,e元素(传递给addClickListener的参数与事件e相同, 不是 (参数传递给this.func

要修复此错误,您需要存储对该类的引用,并在函数定义中使用该引用:

    constructor() {
        this.value = 'something';
        let self = this;
        this.func = function(e) {
             self.doThing(e);
        }
    }

此处self将像this一样被 不会 覆盖,因此我们可以安全地使用它来引用类实例

一个更好的解决方案

当我重新阅读问题时,我意识到您的函数实际上只是在调用另一个函数。因此,为什么不将要最终运行的功能传递给addEventListener

class Sample {
    constructor() {
        this.value = 'something';
    }

    addClickListener(e) {
        e.addEventListener('click', this.doThing, false);
    }

    removeClickListener(e) {
        e.removeEventListener('click', this.doThing, false);
    }

    doThing(p) { 
        //code
    }
}

请注意,这仍然会存在this被调用事件的元素替换的问题,因此在doThing中您不能说this.doOtherThing来调用第二类方法。如果您希望这样做,则需要使用JavaScript的.bind()方法创建一个 绑定方法

class Sample {
    constructor() {
        this.value = 'something';
        this.boundMethod = this.doThing.bind(this);
    }

    addClickListener(e) {
        e.addEventListener('click', this.boundMethod, false);
    }

    removeClickListener(e) {
        e.removeEventListener('click', this.boundMethod, false);
    }

    doThing(p) {
        //code
        this.doOtherThing(p);
        //more code
    }

    doOtherThing(p) {
        //code
    }
}

另一个选择

正如@evolutionxbox指出的那样,您也可以使用箭头功能。此解决方案如下所示:

class Sample {
    constructor() {
        this.value = 'something';
        this.boundMethod = p => { this.doThing(p); };
    }

    addClickListener(e) {
        e.addEventListener('click', this.boundMethod, false);
    }

    removeClickListener(e) {
        e.removeEventListener('click', this.boundMethod, false);
    }

    doThing(p) {
        //code
        this.doOtherThing(p);
        //more code
    }

    doOtherThing(p) {
        //code
    }
}

之所以可行,是因为箭头函数是绑定方法的简写:

x => x + 1;

// Is functionally equivalent to:

(function(x) {
    return x + 1;
}).bind(this);

许多人没有意识到箭头功能包括隐式bind,并且在90%的用例中都没有关系(例如array.map(x => x + 1)并不关心{{ 1}}),但是在类中,这种简写实际上是有价值的,因此在JavaScript类中使用箭头函数以避免覆盖this变量已成为一种相当普遍的模式。