在Javascript中,为什么" this"运算符不一致?
在JavaScript中," this"运算符可以在不同情况下引用不同的事物。
通常,在JavaScript"对象"内的方法中,它是指当前对象。
但是当用作回调时,它成为对调用对象的引用。
我发现这会导致代码出现问题,因为如果我们在JavaScript"对象"中使用方法作为回调函数,则无法确定" 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'); } }
解决方案
回答
我相信这可能是由于closures在Javascript中是如何工作的。
我本人只是想了解闭包。阅读链接的维基百科文章。
这是另一篇包含更多信息的文章。
有人可以确认吗?
回答
一旦从其他上下文中调用了回调方法,我通常会使用一些我称为回调上下文的东西:
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; } }
回答
在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'); } }
回答
" this"对应于函数调用的上下文。对于未作为对象的一部分调用的函数(无"。"运算符),这是全局上下文(网页中的"窗口")。对于称为对象方法(通过。运算符)的函数,它是对象。
但是,我们可以随心所欲。所有函数都有.call()和.apply()方法,可用于通过自定义上下文调用它们。因此,如果我像这样设置对象智利:
var Chile = { name: 'booga', stuff: function() { console.log(this.name); } };
...并调用Chile.stuff(),它将产生明显的结果:
booga
但是,如果我愿意,我可以接受并真正解决:
Chile.stuff.apply({ name: 'supercalifragilistic' });
这实际上很有用...
回答
我们还可以使用Function.Apply(thisArg,argsArray)...其中thisArg确定函数内部的this的值...第二个参数是一个可选参数数组,我们也可以将其传递给函数。
如果我们不打算使用第二个参数,则不要向其传递任何内容。如果将null(或者任何非数组的值)传递给function.apply()的第二个参数,则Internet Explorer将向我们抛出TypeError。
使用我们提供的示例代码,它看起来像:
YAHOO.util.Connect.asyncRequest(method, uri, callBack.Apply(this));
回答
在深入探讨此变量的不可思议之处之前,我们将获得最佳实践的快速建议。如果我们希望Javascript中的OOP能够更紧密地反映传统/经典继承模式,请选择一个框架,学习其怪癖,不要试图变得聪明。如果我们想变得聪明一点,请将javascript作为一种功能语言学习,并避免考虑类之类的事情。
这带来了最重要的事情之一,要牢记有关Javascript的内容,并在没有意义的情况下对自己重复。 Javascript没有类。如果某个东西看起来像一堂课,那是一个聪明的把戏。 Javascript具有对象(不需要破坏性的引号)和功能。 (这不是100%准确的,函数只是对象,但有时将它们视为独立的东西会有所帮助)
此变量添加到函数。每当我们调用一个函数时,该函数都会被赋予一个特定的值,具体取决于我们如何调用该函数。这通常称为调用模式。
有四种方法可以调用javascript中的函数。我们可以将函数作为方法,函数,构造函数以及apply调用。
作为一种方法
方法是添加到对象的函数
var foo = {}; foo.someMethod = function(){ alert(this); }
当作为方法调用时,它将绑定到功能/方法所属的对象。在此示例中,这将绑定到foo。
作为功能
如果我们具有独立功能,则此变量将绑定到"全局"对象,几乎总是在浏览器上下文中的window对象。
var foo = function(){ alert(this); } foo();
这可能是使我们绊倒的原因,但并不难过。许多人认为这是一个错误的设计决定。由于回调是作为函数而不是方法调用的,因此这就是为什么我们看到的行为不一致。
很多人通过做类似的事情来解决这个问题
var foo = {}; foo.someMethod = function (){ var that=this; function bar(){ alert(that); } }
我们定义一个指向该变量。闭包(它本身就是一个话题)可以解决这个问题,因此,如果我们将bar作为回调调用,它仍然具有引用。
作为建设者
我们还可以将函数作为构造函数调用。根据我们使用的命名约定(TestObject
),这也可能就是我们正在做的事情,并且是绊脚石。
我们可以使用关键字new调用一个作为构造函数的函数。
function Foo(){ this.confusing = 'hell yeah'; } var myObject = new Foo();
当作为构造函数调用时,将创建一个新的Object,并将其绑定到该对象。同样,如果我们具有内部函数并将它们用作回调,则将它们作为函数调用,并且此函数将绑定到全局对象。使用" var that = this;"技巧/模式。
有些人认为,构造函数/ new关键字是Java /传统OOP程序员的骨干,可以用来创建类似于类的东西。
使用Apply方法。
最后,每个函数都有一个名为" apply"的方法(是的,函数是Javascript中的对象)。应用可以确定其值,也可以传递参数数组。这是一个无用的示例。
function foo(a,b){ alert(a); alert(b); alert(this); } var args = ['ah','be']; foo.apply('omg',args);
回答
如果我们使用的是原型,则可以使用bind()和bindAsEventListener()来解决该问题。
回答
如果我们使用的是JavaScript框架,则可能有一种方便的方法来处理此问题。例如,在原型中,我们可以调用一个方法并将其范围限定到特定的" this"对象:
var myObject = new TestObject(); myObject.firstMethod.bind(myObject);
注意:bind()返回一个函数,因此我们也可以使用它来预定义类中的回调:
callBack.bind(this);
http://www.prototypejs.org/api/function/bind