在JavaScript中,使用bind()删除作为事件侦听器添加的函数的最佳方法是什么?

例子

(function(){

    // constructor
    MyClass = function() {
        this.myButton = document.getElementById("myButtonID");
        this.myButton.addEventListener("click", this.clickListener.bind(this));
    };

    MyClass.prototype.clickListener = function(event) {
        console.log(this); // must be MyClass
    };

    // public method
    MyClass.prototype.disableButton = function() {
        this.myButton.removeEventListener("click", ___________);
    };

})();

我能想到的唯一方法是跟踪bind添加的每个侦听器。

上面这个方法的例子:

(function(){

    // constructor
    MyClass = function() {
        this.myButton = document.getElementById("myButtonID");
        this.clickListenerBind = this.clickListener.bind(this);
        this.myButton.addEventListener("click", this.clickListenerBind);
    };

    MyClass.prototype.clickListener = function(event) {
        console.log(this); // must be MyClass
    };

    // public method
    MyClass.prototype.disableButton = function() {
        this.myButton.removeEventListener("click", this.clickListenerBind);
    };

})();

还有什么更好的办法吗?


当前回答

已经有一段时间了,但MDN对此有一个超级解释。那比这里的东西更有用。

MDN:: EventTarget。addEventListener -处理程序中“this”的值

它为handleEvent函数提供了一个很好的替代方案。

这是一个带和不带bind的例子:

var Something = function(element) {
  this.name = 'Something Good';
  this.onclick1 = function(event) {
    console.log(this.name); // undefined, as this is the element
  };
  this.onclick2 = function(event) {
    console.log(this.name); // 'Something Good', as this is the binded Something object
  };
  element.addEventListener('click', this.onclick1, false);
  element.addEventListener('click', this.onclick2.bind(this), false); // Trick
}

上面示例中的一个问题是不能使用bind删除侦听器。另一个解决方案是使用一个叫做handleEvent的特殊函数来捕捉任何事件:

其他回答

下面是解决方案:

var o = {
  list: [1, 2, 3, 4],
  add: function () {
    var b = document.getElementsByTagName('body')[0];
    b.addEventListener('click', this._onClick());

  },
  remove: function () {
    var b = document.getElementsByTagName('body')[0];
    b.removeEventListener('click', this._onClick());
  },
  _onClick: function () {
    this.clickFn = this.clickFn || this._showLog.bind(this);
    return this.clickFn;
  },
  _showLog: function (e) {
    console.log('click', this.list, e);
  }
};


// Example to test the solution
o.add();

setTimeout(function () {
  console.log('setTimeout');
  o.remove();
}, 5000);

如果你想使用'onclick',就像上面建议的那样,你可以试试这个:

(function(){
    var singleton = {};

    singleton = new function() {
        this.myButton = document.getElementById("myButtonID");

        this.myButton.onclick = function() {
            singleton.clickListener();
        };
    }

    singleton.clickListener = function() {
        console.log(this); // I also know who I am
    };

    // public function
    singleton.disableButton = function() {
        this.myButton.onclick = "";
    };
})();

我希望这能有所帮助。

我们有一个无法更改的库的问题。Office Fabric UI,这意味着我们不能更改添加事件处理程序的方式。我们解决这个问题的方法是在EventTarget原型上覆盖addEventListener。

这将在objects上添加一个新函数element。

(原帖子:从织物对话框覆盖删除点击处理程序)

        <script>
            (function () {
                "use strict";

                var f = EventTarget.prototype.addEventListener;

                EventTarget.prototype.addEventListener = function (type, fn, capture) {
                    this.f = f;
                    this._eventHandlers = this._eventHandlers || {};
                    this._eventHandlers[type] = this._eventHandlers[type] || [];
                    this._eventHandlers[type].push([fn, capture]);
                    this.f(type, fn, capture);
                }

                EventTarget.prototype.removeAllEventListeners = function (type) {
                    this._eventHandlers = this._eventHandlers || {};
                    if (type in this._eventHandlers) {
                        var eventHandlers = this._eventHandlers[type];
                        for (var i = eventHandlers.length; i--;) {
                            var handler = eventHandlers[i];
                            this.removeEventListener(type, handler[0], handler[1]);
                        }
                    }
                }

                EventTarget.prototype.getAllEventListeners = function (type) {
                    this._eventHandlers = this._eventHandlers || {};
                    this._eventHandlers[type] = this._eventHandlers[type] || [];
                    return this._eventHandlers[type];
                }

            })();
        </script>

正如其他人所说,bind创建了一个新的函数实例,因此除非以某种方式记录事件侦听器,否则无法删除事件侦听器。

为了获得更漂亮的代码风格,你可以将方法函数设置为惰性getter,以便在第一次访问时自动替换为绑定版本:

class MyClass {
  activate() {
    window.addEventListener('click', this.onClick);
  }

  deactivate() {
    window.removeEventListener('click', this.onClick);
  }

  get onClick() {
    const func = (event) => {
      console.log('click', event, this);
    };
    Object.defineProperty(this, 'onClick', {value: func});
    return func;
  }
}

如果ES6箭头函数不受支持,请使用const func = (function(event){…}).bind(this)代替const func = (event) =>{…}。

Raichman Sergey的方法也很好,尤其是在课堂上。这种方法的优点是它更加自我完整,没有其他地方的分离代码。它也适用于没有构造函数或启动器的对象。

jQuery的解决方案:

let object = new ClassName();
let $elem = $('selector');

$elem.on('click', $.proxy(object.method, object));

$elem.off('click', $.proxy(object.method, object));