【问题标题】:In Javascript, why is the "this" operator inconsistent?在Javascript中,为什么“this”运算符不一致?
【发布时间】:2010-09-09 22:53:56
【问题描述】:

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

【问题讨论】:

  • 基于上下文here的神秘这种行为的一个很好的解释

标签: javascript


【解决方案1】:

在我喋喋不休地谈论神奇的 this 变量之前,对最佳实践的快速建议。如果您希望 Javascript 中的面向对象编程 (OOP) 与更传统/经典的继承模式密切相关,请选择一个框架,了解它的怪癖,不要试图变得聪明。如果你想变聪明,把 javascript 作为一种函数式语言来学习,避免考虑类之类的东西。

这带来了关于 Javascript 的最重要的事情之一,当它没有意义时,请向自己重复一遍。 Javascript没有类。如果某些东西看起来像一个类,这是一个聪明的把戏。 Javascript 有对象(不需要嘲讽的引号)和函数。 (这不是 100% 准确,函数只是对象,但有时将它们视为独立的事物会有所帮助)

this 变量附加到函数。每当你调用一个函数时,this 都会被赋予一个特定的值,这取决于你调用函数的方式。这通常称为调用模式。

在 javascript 中有四种调用函数的方法。您可以将函数作为方法函数构造函数apply调用。

作为一种方法

方法是附加到对象的函数

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

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

作为一个函数

如果你有一个独立的函数,this 变量将绑定到“全局”对象,在浏览器上下文中几乎总是 window 对象。

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

这可能是你的绊脚石,但不要难过。许多人认为这是一个糟糕的设计决定。由于回调是作为函数而不是作为方法调用的,这就是为什么您会看到似乎不一致的行为。

许多人通过做类似的事情来解决这个问题,嗯,这个

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

你定义了一个变量that,它指向this。闭包(它自己的一个主题)保留了that,所以如果你调用 bar 作为回调,它仍然有一个引用。

作为构造函数

您也可以将函数作为构造函数调用。根据您使用的命名约定 (TestObject),这也可能是您正在做的事情,并且是您遇到的问题

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

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

当作为构造函数调用时,将创建一个新对象,并且 this 将绑定到该对象。同样,如果您有内部函数并且它们被用作回调,那么您将作为函数调用它们,并且 this 将绑定到全局对象。使用 var that = this; 技巧/模式。

有些人认为constructor/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);

【讨论】:

  • 可能值得补充:还有call方法,类似apply的函数,只是参数不作为数组传递。
  • @Alan,很好的答案,但值得一提的是bind
【解决方案2】:

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

【讨论】:

  • 链接到关于 javascript 闭包的非常好的演示文稿:tinyurl.com/5mrpjv
  • 请记住,“在 JavaScript 中,“this”总是指调用正在执行的函数的对象”这句话在技术上是不正确的。根据调用函数的方式, this 可以有四种不同的上下文。
  • 函数原型不应该放在构造函数之外吗?或将TestObject.prototype 替换为that
【解决方案3】:

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

但是,你可以随心所欲。所有函数都有 .call() 和 .apply() 方法,可用于通过自定义上下文调用它们。所以如果我像这样设置一个对象智利:

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

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

booga

但如果我愿意,我可以接受并真的搞砸了

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

这其实挺有用的……

【讨论】:

    【解决方案4】:

    如果您使用的是 javascript 框架,则可能有一种方便的方法来处理此问题。例如,在 Prototype 中,您可以调用一个方法并将其作用域为特定的“this”对象:

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

    注意:bind() 返回一个函数,因此您也可以使用它来预定义类中的回调:

    callBack.bind(this);
    

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

    【讨论】:

      【解决方案5】:

      我相信这可能是由于 [closures](http://en.wikipedia.org/wiki/Closure_(computer_science) 在 Javascript 中的工作原理。

      我自己刚刚开始掌握闭包。阅读链接的维基百科文章。

      这里是another article,了解更多信息。

      有人能证实这一点吗?

      【讨论】:

        【解决方案6】:

        您也可以使用 Function.Apply(thisArg, argsArray)... 其中 thisArg 确定函数内 this 的值。 ..第二个参数是一个可选参数数组,您也可以将其传递给您的函数。

        如果您不打算使用第二个参数,请不要将任何内容传递给它。如果您将 null(或任何非数组)传递给 function.apply() 的第二个参数,Internet Explorer 将向您抛出 TypeError...

        使用您提供的示例代码,它看起来像:

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            【解决方案8】:

            一旦从其他上下文调用回调方法,我通常会使用我称之为回调上下文的东西:

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

            【讨论】:

              猜你喜欢
              • 2014-06-17
              • 2012-08-23
              • 1970-01-01
              • 1970-01-01
              • 2015-07-31
              • 1970-01-01
              • 1970-01-01
              • 2020-02-18
              • 1970-01-01
              相关资源
              最近更新 更多