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

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

In Javascript, why is the “this” operator inconsistent?

在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将引用触发事件的节点。但如果你有一个对象并在其上调用一个函数,如:

1
myObject.myFunction();

然后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
booga

但如果我想,我可以采取并真正搞砸它:

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

这实际上非常有用......


如果您正在使用javascript框架,可能有一个方便的方法来处理这个问题。例如,在Prototype中,您可以调用方法并将其范围限定为特定的"this"对象:

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

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

1
callBack.bind(this);

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中工作。

我自己就是要掌握封口。阅读链接的维基百科文章。

这是另一篇有更多信息的文章。

那里的任何人都能证实这一点吗?


推荐阅读

    linux命令引用文件名?

    linux命令引用文件名?,工作,系统,信息,命令,数据,文件,时间,灵活,名称,标准,l

    linux调用函数的命令?

    linux调用函数的命令?,系统,代码,策略,上调,时间,设计,通信,网络,设备,项目,

    linux外部命令调用?

    linux外部命令调用?,系统,软件,标准,命令,盘中,管理,外部,进程,程序,内存,lin

    linux怎么调用命令行?

    linux怎么调用命令行?,系统,地址,工具,工作,首页,终端,命令,密码,信息,情况,l

    浏览器调用linux命令?

    浏览器调用linux命令?,系统,信息,人工智能,软件,数据,首次,地址,代码,咨询,

    py调用linux的命令?

    py调用linux的命令?,系统,代码,状态,环境,标准,工具,命令,文件,脚本,终端,lin

    浏览器调用linux命令?

    浏览器调用linux命令?,系统,信息,人工智能,软件,数据,首次,地址,代码,咨询,

    py调用linux的命令?

    py调用linux的命令?,系统,代码,状态,环境,标准,工具,命令,文件,脚本,终端,lin

    linux系统命令调用?

    linux系统命令调用?,系统,单位,工具,工作,管理,地址,权威,密码,电脑,信息,怎

    linux调用上一条命令?

    linux调用上一条命令?,系统,命令,一致,数字,名称,网上,电脑,目录,空格,终端,l

    linux命令行调用程序?

    linux命令行调用程序?,工具,环境,代码,初级,工程,系统,网上,服务,管理,发行,l

    linux使用命令的方法?

    linux使用命令的方法?,系统,信息,工具,标准,数据,命令,左下角,目录,文件夹,

    linux无效对象的命令?

    linux无效对象的命令?,软件,系统,单位,网络,管理,术语,检测,电脑,环境,风险,l

    脚本调用linux命令?

    脚本调用linux命令?,代码,系统,工作,底部,脚本,位置,环境,行用,官网,标准,typ

    调用函数命令linux?

    调用函数命令linux?,系统,管理,网络,通用,统一,观察,地址,代码,设备,地方,怎

    linux内核总调用命令?

    linux内核总调用命令?,工作,地址,系统,信息,管理,策略,命令,目录,时间,基础,

    linux编程调用命令?

    linux编程调用命令?,系统,标准,管理,工作,基础知识,情况,环境,设备,基础,首

    linux命令窗口调用?

    linux命令窗口调用?,系统,工具,首页,终端,命令,数据,盘中,代码,密码,快捷键,

    linux服务器调用命令?

    linux服务器调用命令?,系统,地址,设备,标准,工作,密码,中心,盘中,网络,软件,

    linuxc调用命令框?

    linuxc调用命令框?,系统,工作,标准,情况,设备,环境,命令,函数,语言,程序,如何