jQuery.post回调函数在什么上下文中被调用?

7
假设我们举一个例子:
$(".button").click(function() {

    $.post("commandrunner.php",
        {
            param1: 'value',
            param2: 'value2',
            param3: 'value3'
        },
        function(data, textStatus) {
            $(this).parent().after('<p>button clicked</p>');
        },
        "json"
    );

});

我运行了这段代码,但它没有起作用。在我提出回调函数没有在特定的“.button”上下文中被调用,所以$(this)无用之前,我尝试了几个方法。相反,以下代码可以解决问题:

$(".button").click(function() {
    var thisButton = $(this);

    $.post("commandrunner.php",
        {
            param1: 'value',
            param2: 'value2',
            param3: 'value3'
        },
        function(data, textStatus) {
            thisButton.parent().after('<p>button clicked</p>')
        },
        "json"
    );

});

这感觉有点像一个hack。获取点击按钮的引用的正确方法是什么?任何回调函数在什么上下文中被调用?谢谢!Ali
4个回答

13

您注意到的是 JavaScript 闭包的工作方式。与其他面向对象编程语言相同,当方法被调用时,它有一个“this”,类似于 Java 的 this 或 Ruby 的 self。然而,JavaScript 的 this 非常灵活,并且 jQuery 利用该属性在调用回调时将其设置为有用的值。

在事件的情况下,jQuery 将 this 指针设置为指向绑定事件处理程序的元素。看一下这个:

var hello = function() {
  console.log("Hello " + this);
});

如果您有面向对象的编程背景,您可能会对上面的片段感到困惑: this 指向什么?默认情况下,它将指向全局对象(浏览器中的窗口)。但是您可以轻松地覆盖它:

hello.call("world") // will print "Hello world"

调用方法时,除了传递作为参数的 this 对象外,还可传递多个参数。类似的方法叫做 apply,它也接受一个 this 作为第一个参数,但将参数以数组形式作为第二个参数传入。

现在,我们再来看一下你的示例:

$(".button").click(function() {

    $.post("commandrunner.php",
        {
            param1: 'value',
            param2: 'value2',
            param3: 'value3'
        },
        function(data, textStatus) {
            $(this).parent().after('<p>button clicked</p>')
        },
        "json"
    );

});

问题在于当jQuery再次调用该函数时,它没有使用相同的this。当您创建一个匿名函数时,它将保留在相同作用域中的所有本地变量,但不包括由JavaScript本身设置的this(设置为全局对象)或在调用时被覆盖。

因此,解决方案是将指向this的指针存储在局部变量中,然后将其提供给闭包函数。

如上所述,解决方案是:

$(".button").click(function() {
  var parent = $(this).parent();
  $.post("commandrunner.php",
    {
      param1: 'value',
      param2: 'value2',
      param3: 'value3'
    },
    function() {
      parent.after('<p>button clicked</p>')
    },
    "json"
  );
});

通常情况下,当我存储本地变量时,我会存储最具体的一组元素,以使回调代码更简单。常见的jQuery习惯用法是使用var self = this,这也很好。

此外,请注意JavaScript函数不检查参数的数量,因此如果您不使用它们,则可以将参数留空。它们仍将被传递,但空参数列表将被忽略。


+1 对于设置特定元素而不是整个对象进行设置。 - Luca Matteis
谢谢,这回答了我的问题。顺便说一下,这是一本很棒的书。我从附录中学到了关于JavaScript的所有重要知识!此外,作为一个OO/Java背景的人,我一直在苦苦挣扎闭包。我可以认为上面的例子是一个闭包吗?(即,在帖子回调中的匿名函数可以访问封闭匿名函数范围内的变量,并且因此这是一个“闭包”) - Ali
JavaScript中的所有函数,无论它们是否附加到对象上,都会在创建时封闭在本地变量中。这适用于命名函数(function foo() {})和匿名函数(function() {})。 - Yehuda Katz
哦,谢谢你提醒我关于未使用的参数,我会记住的。 - Ali
顺便提一下:如果你想知道一个函数有多少个显式参数,可以在其上调用 length。例如:(function(x, y) {}).length 将返回 2,(function() {}).length 将返回 0。 - Yehuda Katz

5
根据jQuery API(在“回调函数队列”下),任何JQuery AJAX回调函数中的this对象将自动指向ajax对象的设置或(如果指定)context对象。 不幸的是,在$.post()中无法指定context

你可以在$.ajax中指定一个,例如:$.ajax({ url: "test.html", context: document.body }).done(function() { $( this ).addClass( "done" ); }); - cnlevy

0

这是解决问题的正确方法。

找出上下文最简单的方法是使用Firebugconsole.log(this)——我猜测它可能是XHR对象。


0
关键字“this”绑定到函数的作用域。要引用其他父级作用域,您需要将引用存储在适当的作用域变量中,例如var parentScope = this;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接