在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);
    };

})();

还有什么更好的办法吗?


当前回答

是否使用有界函数并不重要;您可以像删除任何其他事件处理程序一样删除它。如果您的问题是绑定版本是它自己的唯一函数,那么您可以跟踪绑定版本,或者使用不接受特定处理程序的removeEventListener签名(当然,这将删除相同类型的其他事件处理程序)。

(顺便说一句,addEventListener并不适用于所有浏览器;你真的应该使用像jQuery这样的库来跨浏览器地为你做你的事件连接。此外,jQuery还有命名空间事件的概念,它允许你绑定到“click.foo”;当你想删除事件时,你可以告诉jQuery“删除所有foo事件”,而不需要知道特定的处理程序或删除其他处理程序。)

其他回答

是否使用有界函数并不重要;您可以像删除任何其他事件处理程序一样删除它。如果您的问题是绑定版本是它自己的唯一函数,那么您可以跟踪绑定版本,或者使用不接受特定处理程序的removeEventListener签名(当然,这将删除相同类型的其他事件处理程序)。

(顺便说一句,addEventListener并不适用于所有浏览器;你真的应该使用像jQuery这样的库来跨浏览器地为你做你的事件连接。此外,jQuery还有命名空间事件的概念,它允许你绑定到“click.foo”;当你想删除事件时,你可以告诉jQuery“删除所有foo事件”,而不需要知道特定的处理程序或删除其他处理程序。)

虽然@machineghost说的是真的,事件以相同的方式添加和删除,但等式中缺失的部分是:

调用.bind()后创建一个新的函数引用。

参见bind()是否改变函数引用?|如何永久设置?

因此,要添加或删除它,将引用赋值给一个变量:

var x = this.myListener.bind(this);
Toolbox.addListener(window, 'scroll', x);
Toolbox.removeListener(window, 'scroll', x);

这对我来说就像预期的那样。

可以使用ES7:

class App extends React.Component {
  constructor(props){
    super(props);
  }
  componentDidMount (){
    AppStore.addChangeListener(this.onChange);
  }

  componentWillUnmount (){
    AppStore.removeChangeListener(this.onChange);
  }

  onChange = () => {
    let state = AppStore.getState();
    this.setState(state);
  }

  render() {
    // ...
  }

}

已经有一段时间了,但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的特殊函数来捕捉任何事件:

对于那些在从Flux store中注册/删除React组件的监听器时遇到这个问题的人,请将以下代码添加到组件的构造函数中:

class App extends React.Component { constructor(props){ super(props); // it's a trick! needed in order to overcome the remove event listener this.onChange = this.onChange.bind(this); } // then as regular... componentDidMount (){ AppStore.addChangeListener(this.onChange); } componentWillUnmount (){ AppStore.removeChangeListener(this.onChange); } onChange () { let state = AppStore.getState(); this.setState(state); } render() { // ... } }