在Javascript中,为什么“this”运算符不一致?

在JavaScript中,“this”运算符可以在不同情况下引用不同的东西。

通常在JavaScript“对象”内的方法中,它指的是当前对象。

但是当作为callback使用时,它将成为对调用对象的引用。

我发现这会导致代码中的问题,因为如果你在一个JavaScript对象中使用一个方法作为callback函数,你不能判断“this”是指当前的“object”,还是“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'); } } 

在JavaScript中,“this”总是指调用正在执行的函数的对象。 所以如果这个函数被用作一个事件处理函数,那么“this”将引用引发这个事件的节点。 但是,如果你有一个对象,并像这样调用一个函数:

 myObject.myFunction(); 

那么myFunction中的“this”将引用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'); } } 

关于最佳做法的快速build议之前,我喋喋不休地谈论这个variables的魔术。 如果你想在JavaScript中使用面向对象的面向对象的面向对象的方法,可以select一个框架,学习它的怪癖,而不要试图变得聪明。 如果你想变得聪明,学习JavaScript作为一种function性语言,并避免考虑像类的东西。

这引出了关于Javascript的最重要的事情之一,并且在没有意义的时候重复自己。 Javascript没有类。 如果某件事看起来像一个class级,这是一个聪明的把戏。 Javascript有对象 (不需要嘲笑引号)和函数 。 (这不是100%准确的,函数只是对象,但有时可以把它们看作是独立的东西)

这个variables被附加到函数中。 无论何时你调用一个函数,都会给出一个特定的值,这取决于你如何调用函数。 这通常被称为调用模式。

有四种方法可以在javascript中调用函数。 你可以调用函数作为一个方法 ,作为一个函数 ,作为构造函数 ,并与应用程序

作为一种方法

方法是附加到对象的函数

 var foo = {}; foo.someMethod = function(){ alert(this); } 

当作为方法调用时, 将被绑定到函数/方法所属的对象。 在这个例子中,这将被绑定到foo。

作为一个function

如果你有一个独立的函数, 这个variables将被绑定到“全局”对象,几乎总是浏览器上下文中的窗口对象。

  var foo = function(){ alert(this); } foo(); 

这可能是什么绊倒你 ,但不要感觉不好。 许多人认为这是一个糟糕的devise决定。 由于callback是作为函数调用的,而不是作为方法调用的,所以这就是为什么你看到什么似乎是不一致的行为。

很多人通过做这样的事情来解决问题

 var foo = {}; foo.someMethod = function (){ var that=this; function bar(){ alert(that); } } 

你定义一个指向这个的variables。 closures(一个主题都是它自己的)会保持这个状态 ,所以如果你把bar当作callback,它仍然有一个引用。

作为构造函数

你也可以调用一个函数作为构造函数。 基于你使用的命名约定( TestObject ),这也可能是你在做什么,是什么让你绊倒

您使用new关键字作为构造函数调用函数。

 function Foo(){ this.confusing = 'hell yeah'; } var myObject = new Foo(); 

当作为构造函数调用时,将创build一个新的Object,并将其绑定到该对象。 同样,如果你有内部函数,并且它们被用作callback函数,那么你将会把它们作为函数调用,并且将被绑定到全局对象。 使用那个var that = this; 招/模式。

有些人认为构造函数/ new关键字是抛出到Java /传统的OOP程序员的骨头,以创build类似于类的东西。

通过应用方法。

最后,每个函数都有一个名为apply的方法(是的,函数是Javascript中的对象)。 Apply可以让你确定这个值是什么,也可以让你传入一个参数数组。 这是一个没用的例子。

 function foo(a,b){ alert(a); alert(b); alert(this); } var args = ['ah','be']; foo.apply('omg',args); 

this对应于函数调用的上下文。 对于不作为对象(否操作符)的一部分调用的函数,这是全局上下文(网页中的window )。 对于称为对象方法的函数(通过。运算符),它是对象。

但是,你可以做任何你想要的。 所有的函数都有.call()和.apply()方法,可以用它们用自定义上下文来调用它们。 所以,如果我设立一个智利这样的对象:

 var Chile = { name: 'booga', stuff: function() { console.log(this.name); } }; 

…并调用Chile.stuff(),它会产生明显的结果:

 booga 

但是,如果我想,我可以采取,并真的拧它

 Chile.stuff.apply({ name: 'supercalifragilistic' }); 

这其实很有用…

如果你使用的是JavaScript框架,可能有一个方便的方法来处理这个问题。 例如,在Prototype中,您可以调用一个方法并将其范围调整到特定的“this”对象:

 var myObject = new TestObject(); myObject.firstMethod.bind(myObject); 

注意:bind()返回一个函数,所以你也可以使用它来在你的类中预先调用callback函数。

 callBack.bind(this); 

http://www.prototypejs.org/api/function/bind

我相信这可能是由于Javascript中的[闭包]( http://en.wikipedia.org/wiki/Closure_ ( computer_science)这个想法是如何工作的。

我正在自己处理闭包。 阅读链接的维基百科文章。

这是另一篇文章 ,更多的信息。

任何人都可以证实这一点?

你也可以使用Function.Apply( thisArgargsArray )…其中thisArg决定你的函数内的值…第二个参数是一个可选的参数数组,你也可以传递给你的函数。

如果你不打算使用第二个参数,不要传递任何东西。 如果您将null (或不是数组的任何内容)传递给function.apply()的第二个参数,则Internet Explorer将在您处引发TypeError …

用你给它的例子代码看起来像这样:

 YAHOO.util.Connect.asyncRequest(method, uri, callBack.Apply(this)); 

如果你正在使用Prototype,你可以使用bind()和bindAsEventListener()来解决这个问题。

只要从其他上下文中调用callback方法,我通常会使用我称之为callback上下文的东西:

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