在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);
我相信这可能是由于Javascript中的[闭包]( http://en.wikipedia.org/wiki/Closure_ ( computer_science)这个想法是如何工作的。
我正在自己处理闭包。 阅读链接的维基百科文章。
这是另一篇文章 ,更多的信息。
任何人都可以证实这一点?
你也可以使用Function.Apply( thisArg , argsArray )…其中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; } }