Passing 'this' and argument to addEventListener function without using bind

后端 未结 4 1790
独厮守ぢ
独厮守ぢ 2021-01-03 05:18

Subsequent to removeEventListener in bootstrapped addon not working when addon disabled, I am exploring other possibilities.

Beside using bind() and cac

相关标签:
4条回答
  • 2021-01-03 05:28

    Since we're talking restartless add-ons... A lot of restartless add-ons use unload and unloadWindow helper functions, to make it easier to implement shutdown properly and also help with stuff like addEventListener, so bear with me for a bit.

    The helpers - unload

    First, unload is a helper function that you pass another function to, that will be run upon shutdown (or can be called manually). Most implementations are extremely similar to this:

    var unloaders = []; // Keeps track of unloader functions.
    
    function unload(fn) {
      if (typeof(fn) != "function") {
        throw new Error("unloader is not a function");
      }
      unloaders.push(fn);
      return function() {
        try {
          fn();
        }
        catch (ex) {
          Cu.reportError("unloader threw " + fn.toSource());
          Cu.reportError(ex);
        }
        unloaders = unloaders.filter(function(c) { return c != fn; });
      };
    }
    

    You'd then hook up shutdown to do the right thing:

    function shutdown() {
      ...
      for (let i = unloaders.length - 1; i >= 0; --i) {
        try {
          unloaders[i]();
        }
        catch (ex) {
          Cu.reportError("unloader threw on shutdown " + fn.toSource());
          Cu.reportError(ex);
        }
      }
      unloaders.length = 0;
    }
    

    Using unload

    Now you can do stuff like:

    function startup() {
      setupSomething();
      unload(removeSomething);
    
      setupSomethingElse();
      var manualRemove = unload(removeSomethingElse);
      ...
      if (condition) {
        manualRemove();
      }
    }
    

    The helpers - unloadWindow

    You'll usually want to create a second function unloadWindow to unload stuff when either your add-on is shut down or the window gets closed, whatever happens first. Not removing stuff when the window gets closed can be very tricky, and create Zombie compartments of your bootstrap.js and/or code modules very easily (this is from experience writing and reviewing restartless add-ons).

    function unloadWindow(window, fn) {
      let handler = unload(function() {
        window.removeEventListener('unload', handler, false);
        try {
          fn();
        }
        catch (ex) {
          Cu.reportError("window unloader threw " + fn.toSource());
          Cu.reportError(ex);
        }
      });
      window.addEventListener('unload', handler, false);
    };
    

    (Some people might want to "optimize" this, as to have only one "unload" handler, but usually you only have so unloadWindow calls that it won't matter.)

    Putting it all together

    Now you can .bind stuff and do whatever and let the the unloader closures keep track of it. Also, you can use this to keep your shut down code next to your initialization code, which might increase readability.

    function setupWindow(window, document) {
      var bound = this.contextPopupShowing.bind(this);
      contextMenu.addEventListener('popupshowing', bound, false);
      unloadWindow(window, function() {
        contextMenu.removeEventListener('popupshowing', bound, false);
      });
    
      // Or stuff like
      var element = document.createElement(...);
      contextMenu.appendChild(element);
      unloadWindow(window, function() {
        contextMenu.removeChild(element);
      });
    
      // Or just combine the above into a single unloader
      unloadWindow(window, function() {
        contextMenu.removeEventListener('popupshowing', bound, false);
        contextMenu.removeChild(element);
      });
    }
    
    0 讨论(0)
  • 2021-01-03 05:40

    http://2ality.com/2013/06/auto-binding.html

    var listener = myWidget.handleClick.bind(myWidget);
    domElement.addEventListener('click', listener);
    ...
    domElement.removeEventListener(listener);
    
    0 讨论(0)
  • 2021-01-03 05:50

    You don't have to use bind for addEventListener. You can use handleEvent. It was in that topic I linked you too:

    Removing event listener which was added with bind

    MDN :: EventTarget.addEventListener - The value of "this" within the handler

    handleEvent is actually a common way the javascript code in the firefox codebase does it.

    Copied straight from MDN:

    var Something = function(element) {
      this.name = 'Something Good';
      this.handleEvent = function(event) {
        console.log(this.name); // 'Something Good', as this is the Something object
        switch(event.type) {
          case 'click':
            // some code here...
            break;
          case 'dblclick':
            // some code here...
            break;
        }
      };
    
      // Note that the listeners in this case are this, not this.handleEvent
      element.addEventListener('click', this, false);
      element.addEventListener('dblclick', this, false);
    
      // You can properly remove the listners
      element.removeEventListener('click', this, false);
      element.removeEventListener('dblclick', this, false);
    }
    

    Where I mostly use bind is when doing a for loop and I make anonymous functions with something in the array like arr[i]. If I don't bind it then it always just takes the last element of the array, I have no idea why, and I hate it, so then I go to using [].forEach.call(arr, function(arrI).

    0 讨论(0)
  • 2021-01-03 05:51

    Before bind() was supported you had to save a reference to this outside the function. Then pass in a function that can forward the invocation the way you want.

    var self = this;
    contextMenu.addEventListener('popupshowing', function() {
         self.contextPopupShowing.apply(self, arguments);
    }, false);
    

    In this case we use apply to set the context to self, our saved version of this, and to send it whatever arguments were passed to the anonymous function via the magic arguments keyword that contains the list of arguments that a function was passed when invoked.

    0 讨论(0)
提交回复
热议问题