为什么要在构造函数中定义事件处理程序成员函数(内联)以便使用“unbind”

Why define event handler member functions (inline) inside the constructor function in order to work with 'unbind'?

本文关键字:内联 unbind 函数 构造函数 定义 成员 程序 事件处理 为什么      更新时间:2023-09-26

在研究签名板小部件的源代码时,我在构造函数中找到了以下代码片段(特别注意以下代码片段中的注释):

var SignaturePad = function (canvas, options) {
    ...
    // MY QUESTION IS ABOUT THE FOLLOWING CODE COMMENT!!!
    // v v v
    // we need add these inline so they are available to unbind while still having
    // access to 'self' we could use _.bind but it's not worth adding a dependency
    this._handleMouseDown = function (event) {
        if (event.which === 1) {
            self._mouseButtonDown = true;
            self._strokeBegin(event);
        }
    };
    // ... other event handlers here
    ...
}

。为了完整地为上述代码提供上下文,稍后将事件处理程序绑定为事件侦听器:

SignaturePad.prototype._handleMouseEvents = function () {
    ...
    this._canvas.addEventListener("mousedown", this._handleMouseDown);
    ...
};

从上面的代码片段中,您可以看到注释:

我们需要内联添加这些内容,以便它们可以在解锁的同时仍然可以访问"self"

我们可以使用 _.bind 但不值得添加依赖项'

我对此挠头。 为什么在解除绑定时需要访问self(我认为"取消绑定"意味着分离事件侦听器,但如果我错了,请纠正我)?

换句话说,我想理解上面的代码注释,以便我可以确定我完全理解了这段代码中的 JavaScript 和/或事件绑定。

绑定处理程序时,该代码中的.addEventListener调用接收函数引用。为了使用 .removeEventListener 取消绑定,您需要传递对同一函数处理程序的引用。

由于 SignaturePad 构造函数为每个实例创建一个新的、唯一的(尽管相同)函数,并绑定该函数,因此它们需要保留对该函数的引用,以便以后取消绑定。因此,他们将其直接放在物体上以备后用。

他们在构造函数中创建这些处理程序的原因是,他们希望它们能够引用已创建的SignaturePad实例。因此,他们创建了一个var self = this变量,并在构造函数引用self中创建函数。如果处理程序在.prototype上,那么鉴于他们的方法,该共享处理程序将无法引用原始对象。


下面是其代码的截断版本,演示如何使用 EventListener 接口:

var SignaturePad = function(canvas, options) {
  this._handleMouseEvents();
};
// Implements the EventListener interface
SignaturePad.prototype.handleEvent = function(event) {
  switch (event.type) {
    case "mousedown":
      this._handleMouseDown(event)
      break
    case "mousemove":
      this._handleMouseMove(event)
      break
    case "mouseup":
      this._handleMouseUp(event)
      break
    default:
      console.log("Unbound event type:", event.type)
  }
}
SignaturePad.prototype._handleMouseDown = function(event) {
  if (event.which === 1) {
    this._mouseButtonDown = true;
    this._strokeBegin(event);
  }
};
SignaturePad.prototype._handleMouseMove = function(event) {
  if (this._mouseButtonDown) {
    this._strokeUpdate(event);
  }
};
SignaturePad.prototype._handleMouseUp = function(event) {
  if (event.which === 1 && this._mouseButtonDown) {
    this._mouseButtonDown = false;
    this._strokeEnd(event);
  }
};
SignaturePad.prototype._strokeUpdate = function(event) {
  console.log("stroke update");
};
SignaturePad.prototype._strokeBegin = function(event) {
  console.log("stroke begin");
};
SignaturePad.prototype._strokeEnd = function(event) {
  console.log("stroke end");
};
SignaturePad.prototype._handleMouseEvents = function() {
  this._mouseButtonDown = false;
  this._canvas.addEventListener("mousedown", this);
  this._canvas.addEventListener("mousemove", this);
  document.addEventListener("mouseup", this);
};

所以你可以看到添加了handleEvent方法,我们实际上并没有使用 .addEventListener 绑定任何函数。相反,我们将引用绑定到SignaturePad对象本身。

当事件发生时,调用 handleEvent 方法的值为 this 指向我们绑定SignaturePad对象。我们仍然可以通过 event.currentTarget .

因此,这使我们能够在.prototype上重用函数,并为我们提供所需的所有对象引用。当然,取消绑定也是以同样的方式完成的,只是我们传递了绑定到.removeEventListener的对象。