2011-07-18 63 views
2

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

所以我

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

然而,当我尝试使用此代码(增加了一些对象后),我不断收到that.theObjects是未定义的错误。它是指for循环中的行。

+0

line'var mySingleton = {theObjects = []; }'应该是'var mySingleton = {theObjects:[]}'。不知道这个问题虽然 –

+0

你只是写了两次相同的事情... – fletom

+0

哦,对不起,我在我的计算器问题中犯了一个错字。这在我的代码中确实是冒号,所以这不是问题。我会相应地编辑我的帖子。 – achow

回答

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有“打探”元素的情况下这是行不通的。为此,请使用我原来的答案。

如果您之前需要支持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 = thisvar 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); 
+0

但是,然后getMouseHandler现在是一个生产函数对象的工厂... – achow

+0

@highwind - 确实。点击处理程序是函数对象。另一种方法是在闭包中创建整个对象。 – gilly3

+0

是的,但是对于处理程序只有一个函数对象,而不是生成它们的工厂会更高效吗? – achow

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))); 
    }; 
    }; 
+0

(对不起,不知道如何在注释中放置代码块...) 如果mySingleton位于全局名称空间中,则“mySingleton.theObjects”将起作用。所以它不会推广到例如在函数中声明的“var mySingleton”。如何获得一个通用的解决方案? 至于“做这种绑定的一种常见模式”,我的印象是创建一个闭包并将“this”绑定到“this”是做事的常用方式(按照Crockford)。为什么我的代码不工作。我拉我的头发,因为我简直不明白为什么它不起作用... – achow

+0

@highwind - 你的代码不起作用,因为创建'mouseHandler'的匿名函数在全局上下文中运行,所以''那个'最后提到'this'。看到我的答案。 – gilly3

+0

@ gilly3 - 这很奇怪。我想我会遇到这样一个愚蠢的错误。哦,我看到你做出了答案。 – achow

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 

最后,你可以强制this别的东西使用callapply

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

外卖是this确定在runti我基于如何调用函数的上下文。通常,允许您创建“类”的框架通过以您的名义隐式应用绑定模式来从您那里抽象出这些细节。这就是为什么它曾经工作,而不再是如此。

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

这里有一篇文章几年前我关于这个问题写道,进入更多细节:http://trephine.org/t/index.php?title=Understanding_JavaScript%27s_this_keyword

希望这有助于!

+1

非常感谢 – 2013-07-23 08:31:30

相关问题