传递对象的成员函数时绑定“this”

时间:2011-07-18 21:40:27

标签: javascript singleton closures

我定义了一个“类”,并且只创建了一个实例。该实例拥有一个最终被传递的成员函数(它是一个鼠标处理程序,但这并不重要)。由于我只会创建一个“类”的实例,所以我决定使用对象文字将其重写为单例。

所以我有

var mySingleton = {
    theObjects : [];
}

mySingleton.mouseHandler = (function() {
    var that = this;
    return function (e) {
        for (var indx = 0; indx < that.theObjects.length; indx++) {
            // do something to that.theObjects[indx];
        }
    }
}());

mySingleton.addObject = function(newObj) {
    this.theObjects.push(newObj);
}

然而,当我尝试使用这段代码时(在添加几个对象之后),我不断得到一个.theObjects是未定义的错误。它指的是for循环中的行。

3 个答案:

答案 0 :(得分:7)

2015年更新 - 使用Function.bind()指定功能中this的值。然后,您可以使用that

,而不是使用this
mySingleton.mouseHandler = function (e) {
    for (var indx = 0; indx < this.theObjects.length; indx++) {
        // do something to this.theObjects[indx];
    }
}.bind(mySingleton);

如果您希望mouseHandler具有'moused'元素的上下文,则此方法无效。为此,请使用下面的原始答案。

如果您之前需要支持IE8或(天堂禁止),则需要使用polyfill


由于您正在调用立即创建mouseHandler的函数,因此它在window而非mySingleton的上下文中运行。因此that是指window。不要立即调用它,只需将其更改为方法,以便它在mySingleton的上下文中运行:

mySingleton.getMouseHandler = function() {
    var that = this;
    return function() { ... };
};
myElement.onclick = mySingleton.getMouseHandler();

当然,由于您已经在使用单例,因此您可以直接引用它。在您的点击处理程序中,检查that.theObjects,而不是检查mySingleton.theObjects。或者,在mouseHandler中将var that = this更改为var that = mySingleton

编辑或者,在调用它时将上下文传递给您的匿名函数:

mySingleton.mouseHandler = (function() {
    var that = this;
    return function (e) {
        for (var indx = 0; indx < that.theObjects.length; indx++) {
            // do something to that.theObjects[indx];
        }
    }
}).call(mySingleton);

答案 1 :(得分:1)

有一些流行的方法可以做到这一点。首先,超简单的解决方案是直接引用mySingleton并绕过与this相关的混淆。而不是that.theObjects只做mySingleton.theObjects而继续你的生活,事情会很好。

但是,这种绑定有一个共同的模式。这是how underscore.js does it

查看annoted source to underscore,您会在哪里找到

 _.bind = function(func, obj) {
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    var args = slice.call(arguments, 2);
    return function() {
      return func.apply(obj, args.concat(slice.call(arguments)));
    };
  };

答案 2 :(得分:0)

到目前为止,其他答案也是正确的。在这里提供我的观点以防万一。

理解代码无法按预期运行的原因的关键是要了解this在JavaScript中的工作原理。问题是this取决于函数的调用方式。

首先,如果您以方法样式调用该函数,this就是您所期望的:

mySingleton.mouseHandler(); // this === mySingleton

如果你将函数附加到esle上,那也可以。

var anotherSingleton = {};
anotherSingleton.foo = mySingleton.mouseHandler;
anotherSingleton.foo(); // this === anotherSingleton

如果分离该函数,this将成为全局范围对象(window

var foo = mySingleton.mouseHandler;
foo(); // this === window

最后,您可以使用thiscall强制apply成为其他内容:

var randomThingy = {};
mySingleton.mouseHandler.call(randomThingy); // this === randomThingy

需要注意的是,this是在运行时根据函数调用的上下文确定的。通常,允许您通过隐式地代表您应用绑定模式,使“类”从您那里抽象出这些细节的框架。这就是它过去工作的原因,而不再适用。

正如其他人所提到的,您可以更改处理程序以通过其范围名称(mySingleton)引用变量,或者按照讨论的方式绑定它。

这是几年前我就这个主题撰写的一篇文章,详细介绍了http://trephine.org/t/index.php?title=Understanding_JavaScript%27s_this_keyword

希望这有帮助!