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

时间:2020-03-05 18:57:56  来源:igfitidea点击:

在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