(javascript)为什么我需要为事件处理程序使用wrap函数?

时间:2009-11-20 05:54:07

标签: javascript closures event-handling wrap-function

我试图理解为什么在下面的代码中我需要Dragger.prototype.wrap以及为什么我不能直接使用事件处理方法:

function Dragger(id) {
    this.isMouseDown = false;
    this.element = document.getElementById(id);
    this.element.onmousedown = this.wrap(this, "mouseDown");
}

Dragger.prototype.wrap = function(obj, method) {
    return function(event) {
        obj[method](event);
    }
}

Dragger.prototype.mouseDown = function(event) {
    this.oldMoveHandler = document.body.onmousemove;
    document.onmousemove = this.wrap(this, "mouseMove");
    this.oldUpHandler = document.body.onmousemove;
    document.onmouseup = this.wrap(this, "mouseUp");
    this.oldX = event.clientX;
    this.oldY = event.clientY;
    this.isMouseDown = true;
}

Dragger.prototype.mouseMove = function(event) {
    if (!this.isMouseDown) {
        return;
    }
    this.element.style.left = (this.element.offsetLeft
            + (event.clientX - this.oldX)) + "px";
    this.element.style.top = (this.element.offsetTop
            + (event.clientY - this.oldY)) + "px";
    this.oldX = event.clientX;
    this.oldY = event.clientY;
}

Dragger.prototype.mouseUp = function(event) {
    this.isMouseDown = false;
    document.onmousemove = this.oldMoveHandler;
    document.onmouseup = this.oldUpHandler;
}

我被告知这是因为this在没有它的情况下发生了变化,但我不明白为什么this会发生变化,为什么包裹函数会阻止它发生变化,以及this会发生什么变化没有包装功能。

2 个答案:

答案 0 :(得分:7)

你需要包装它们,因为当一个函数被用作事件处理程序时,this关键字指的是触发事件的DOM元素,如果你不包装它,你就没有访问Dragger对象的实例成员,例如this.isMouseDown

例如:

假设你有一个按钮:

<input type="button" id="buttonId" value="Click me" />

你有以下对象:

var obj = {
  value: 'I am an object member',
  method: function () {
    alert(this.value);
  }
}

如果你打电话:

obj.method();

您会看到value对象的obj成员中包含文字的提醒('我是对象成员')。

如果您使用obj.method函数作为事件处理程序:

document.getElementById('buttonId').onclick = obj.method;

当用户点击该按钮时,它会提醒'点击我'

为什么呢? 因为当点击事件被触发时,obj.method将执行,this关键字指向DOM元素,它将提醒'点击我',因为该按钮包含value成员。

您可以查看上面运行here的代码段。


对于上下文执行,我始终保持关闭bind函数:

// The .bind method from Prototype.js
if (!Function.prototype.bind) {
  Function.prototype.bind = function(){
    var fn = this, args = Array.prototype.slice.call(arguments),
             object = args.shift();
    return function(){
      return fn.apply(object,
        args.concat(Array.prototype.slice.call(arguments)));
    };
  };
}

它允许您包装任何函数,强制执行上下文。作为第一个参数,它接收将用作this的对象,其余的可选参数是将在此处调用包装的函数中心代码。

在按钮示例中,我们可以将其用作:

document.getElementById('buttonId').onclick = obj.method.bind(obj);

它在很多情况下都非常有用,它将作为ECMAScript 5的一部分引入。

答案 1 :(得分:3)

CMS gave a good answer关于this在不同情境中的价值。但是作为旁注,这里有一个方便的函数,如果你没有使用库或使用没有库的库,你可以使用它来概括Dragger.wrap(类似于dojo.hitch)的效果。有这样一个工具:

var lockContext = function(context, callback) {
    return function() {
        callback.apply(context, arguments);
    }
};