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

时间:2008-09-17 04:46:25

标签: javascript

在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');
            }
        }

8 个答案:

答案 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方法。

最后,每个函数都有一个名为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);

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

答案 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()来解决这个问题。