在JavaScript中,“this”运算符可以在不同的场景下引用不同的东西。
通常在JavaScript“对象”中的方法中,它引用当前对象。
但是当用作回调时,它将成为对调用对象的引用。
我发现这会导致代码出现问题,因为如果你在JavaScript“对象”中使用一个方法作为回调函数,你无法判断“this”是指当前的“对象”还是“this”指的是调用对象。
有人可以澄清如何解决这个问题的用法和最佳做法吗?
function TestObject() {
TestObject.prototype.firstMethod = function(){
this.callback();
YAHOO.util.Connect.asyncRequest(method, uri, callBack);
}
TestObject.prototype.callBack = function(o){
// do something with "this"
//when method is called directly, "this" resolves to the current object
//when invoked by the asyncRequest callback, "this" is not the current object
//what design patterns can make this consistent?
this.secondMethod();
}
TestObject.prototype.secondMethod = function() {
alert('test');
}
}
答案 0 :(得分:82)
在我喋喋不休地讨论魔法此变量之前,请先咨询最佳做法。如果你想在Javascript中使用面向对象编程(OOP)来密切反映更传统/经典的继承模式,那么选择一个框架,学习它的怪癖,不要试图变得聪明。如果你想变聪明,可以学习javascript作为一种函数式语言,并避免考虑像类这样的东西。
这引出了一个关于Javascript最重要的事情,并在没有意义时重复自己。 Javascript没有类。如果看起来像一个类,这是一个聪明的把戏。 Javascript包含对象(不需要嘲弄引用)和功能。 (这不是100%准确,函数只是对象,但将它们视为单独的东西有时会有所帮助)
此变量附加到函数。每当您调用一个函数时,此都会被赋予一定的值,具体取决于您调用该函数的方式。这通常称为调用模式。
有四种方法可以在javascript中调用函数。您可以将该函数作为方法,作为函数,作为构造函数调用,并使用 apply 。< / p>
方法是附加到对象
的函数var foo = {};
foo.someMethod = function(){
alert(this);
}
当作为方法调用时, this 将绑定到函数/方法所属的对象。在这个例子中,这将绑定到foo。
如果你有一个独立的功能,这个变量将被绑定到“全局”对象,几乎总是在浏览器的上下文中的 window 对象。
var foo = function(){
alert(this);
}
foo();
这可能会让你沮丧,但不要感觉不好。很多人认为这是一个糟糕的设计决定。由于回调是作为函数而不是作为方法调用的,因此您可能会看到看似不一致的行为。
很多人通过这样做来解决这个问题,嗯,这个
var foo = {};
foo.someMethod = function (){
var that=this;
function bar(){
alert(that);
}
}
您定义一个 的变量,指向 this 。关闭(它自己的一个主题)保持that
,所以如果你把bar称为回调,它仍然有一个引用。
您还可以将函数作为构造函数调用。根据您正在使用的命名约定(TestObject
),这也可能就是您正在做的事情,并且正在绊倒您。
使用new
关键字调用函数作为构造函数。
function Foo(){
this.confusing = 'hell yeah';
}
var myObject = new Foo();
当作为构造函数调用时,将创建一个新的Object, this 将绑定到该对象。同样,如果你有内部函数并且它们被用作回调,你将把它们作为函数调用,而这个将被绑定到全局对象。使用var that = this;
技巧/模式。
有些人认为构造函数/ new关键字是Java /传统OOP程序员抛出的一个骨骼,可以创建类似于类的东西。
最后,每个函数都有一个名为apply
的方法(是的,函数是Javascript中的对象)。 Apply允许您确定 this 的值,并允许您传入一组参数。这是一个无用的例子。
function foo(a,b){
alert(a);
alert(b);
alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);
答案 1 :(得分:12)
在JavaScript中,this
始终引用调用正在执行的函数的对象。因此,如果该函数被用作事件处理程序,this
将引用触发该事件的节点。但是如果你有一个对象并在其上调用一个函数,如:
myObject.myFunction();
然后this
内的myFunction
会引用myObject
。它有意义吗?
要绕过它,你需要使用闭包。您可以按如下方式更改代码:
function TestObject() {
TestObject.prototype.firstMethod = function(){
this.callback();
YAHOO.util.Connect.asyncRequest(method, uri, callBack);
}
var that = this;
TestObject.prototype.callBack = function(o){
that.secondMethod();
}
TestObject.prototype.secondMethod = function() {
alert('test');
}
}
答案 2 :(得分:3)
this
对应于函数调用的上下文。对于未作为对象一部分调用的函数(无.
运算符),这是全局上下文(网页中为window
)。对于称为对象方法的函数(通过。运算符),它就是对象。
但是,无论你想要什么,你都可以做到。所有函数都有.call()和.apply()方法,可用于使用自定义上下文调用它们。所以如果我像这样建立一个智利对象:
var Chile = { name: 'booga', stuff: function() { console.log(this.name); } };
...并调用Chile.stuff(),它会产生明显的结果:
booga
但是,如果我想,我可以采取真正搞砸:
Chile.stuff.apply({ name: 'supercalifragilistic' });
这实际上非常有用......
答案 3 :(得分:1)
如果你正在使用javascript框架,可能有一个方便的方法来处理这个问题。例如,在Prototype中,您可以调用方法并将其范围限定为特定的“this”对象:
var myObject = new TestObject();
myObject.firstMethod.bind(myObject);
注意:bind()返回一个函数,因此您也可以使用它来预先调整类中的回调:
callBack.bind(this);
答案 4 :(得分:0)
我认为这可能是由于[闭包]的想法(http://en.wikipedia.org/wiki/Closure_(computer_science)如何在Javascript中工作。
我自己正在处理闭包问题。阅读链接的维基百科文章。
以下是another article的更多信息。
那里的任何人都能证实这一点吗?
答案 5 :(得分:0)
一旦从其他上下文调用回调方法,我通常会使用我称之为回调上下文的东西:
var ctx = function CallbackContext()
{
_callbackSender
...
}
function DoCallback(_sender, delegate, callbackFunc)
{
ctx = _callbackSender = _sender;
delegate();
}
function TestObject()
{
test = function()
{
DoCallback(otherFunc, callbackHandler);
}
callbackHandler = function()
{
ctx._callbackSender;
//or this = ctx._callbacjHandler;
}
}
答案 6 :(得分:0)
您还可以使用Function.Apply( thisArg , argsArray )...其中thisArg确定函数中 this 的值。 ..第二个参数是一个可选的参数数组,您也可以将其传递给您的函数。
如果您不打算使用第二个参数,请不要传递任何内容。如果您将 null (或任何非数组)传递给function.apply()的第二个参数,Internet Explorer将抛出一个TypeError ...
使用示例代码,它看起来像:
YAHOO.util.Connect.asyncRequest(method, uri, callBack.Apply(this));
答案 7 :(得分:0)
如果你使用Prototype,可以使用bind()和bindAsEventListener()来解决这个问题。