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