jQuery如何在回调函数中传递更多参数

261
有没有办法在jQuery中将更多数据传递到回调函数中?
我有两个函数,我想要回调函数例如$.post,能够传入AJAX调用的结果数据以及一些自定义参数。
function clicked() {
    var myDiv = $("#my-div");
    // ERROR: Says data not defined
    $.post("someurl.php",someData,doSomething(data, myDiv),"json"); 
    // ERROR: Would pass in myDiv as curData (wrong)
    $.post("someurl.php",someData,doSomething(data, myDiv),"json"); 
}

function doSomething(curData, curDiv) {

}

我希望能够将自己的参数传递给回调函数,同时还要包括从AJAX调用返回的结果。


14
值得一提的是,自 jQuery 1.4 版本以来,jQuery.ajax() 函数就已经增加了上下文设置(http://jquery14.com/day-01/jquery-14)。在这里可以查看它的使用示例:https://dev59.com/cm435IYBdhLWcg3w-1Z_#5097214。 - russau
我解决了我的问题,在 AJAX 完成后返回数据,然后再执行某些操作。 - Onaiggac
14个回答

347
解决方案是通过闭包绑定变量。

作为更基本的示例,这里有一个接收并调用回调函数的示例函数,以及一个示例回调函数:

function callbackReceiver(callback) {
    callback("Hello World");
}

function callback(value1, value2) {
    console.log(value1, value2);
}

这将调用回调函数并提供一个参数。现在您想提供另一个参数,因此您将回调函数包装在闭包中。

callbackReceiver(callback);     // "Hello World", undefined
callbackReceiver(function(value) {
    callback(value, "Foo Bar"); // "Hello World", "Foo Bar"
});

或者更简单的使用ES6 箭头函数:

callbackReceiver(value => callback(value, "Foo Bar")); // "Hello World", "Foo Bar"
关于你具体的例子,我没有使用过jQuery中的.post函数,但是快速浏览文档表明回调应该是一个函数指针,具有以下签名:
function callBack(data, textStatus, jqXHR) {};

因此,我认为解决办法如下:

var doSomething = function(extraStuff) {
    return function(data, textStatus, jqXHR) {
        // do something with extraStuff
    };
};

var clicked = function() {
    var extraStuff = {
        myParam1: 'foo',
        myParam2: 'bar'
    }; // an object / whatever extra params you wish to pass.

    $.post("someurl.php", someData, doSomething(extraStuff), "json");
};

发生了什么?

在最后一行,调用了doSomething(extraStuff)并且该调用的结果是一个函数指针

因为extraStuff作为参数传递给了doSomething,所以它在doSomething函数内部的作用域之内。

extraStuffdoSomething返回的匿名内部函数中被引用时,它被闭包绑定到外部函数的extraStuff参数。即使在doSomething返回之后,这也是正确的。

我没有测试上述代码,但是我在过去24小时内编写了非常相似的代码,并且按照我描述的方式工作。

当然,您可以根据个人喜好/编码标准传递多个变量而不是单个'extraStuff'对象。


7
不对。这两个函数声明应用不同的范围规则。行为因JavaScript运行时(即浏览器)而异。还要尝试比较在Firebug中显示的堆栈跟踪/断点中的函数名称。 - Ihor Kaharlichenko
1
Ihor:你对两种声明方式之间的区别完全正确。这里有一个很好的解释:https://dev59.com/s3RC5IYBdhLWcg3wUvQS#336868 - Bradley Dwyer
如果我想避免嵌套函数以解决性能问题怎么办? - Iulian Onofrei
2
由于示例的特定性,对于试图将其应用于更一般问题的用户来说,这可能有点难以理解。我添加了一个简化的示例,使其更易于理解,还添加了一个箭头函数示例,以防止此问题关闭为新发布的建议重复问题。如果您觉得我的编辑偏离了您答案的原始意图,或者在任何其他方面不适当,请随时回滚它。 - user4639281
4
这个回答的第4版正在Meta上进行讨论。 - bjb568
显示剩余4条评论

84

使用 doSomething(data, myDiv) 时,实际上是调用该函数而不是对其进行引用。

你可以直接传递 doStomething 函数,但必须确保它具有正确的签名。

如果您想保持 doSomething 的原样,可以在其调用外部包装一个匿名函数。

function clicked() {
    var myDiv = $("#my-div");
    $.post("someurl.php",someData, function(data){ 
      doSomething(data, myDiv)
    },"json"); 
}

function doSomething(curData, curDiv) {
    ...
}

在匿名函数的代码内部,您可以使用在封闭作用域中定义的变量。这就是JavaScript作用域的工作方式。


9
我认为这是最好的解决方案,为了清晰起见,在doSomething函数内不需要额外的return function(...){...}函数。我在这里找到了另一个相同概念的清晰示例:http://theelitist.net/passing-additional-arguments-to-a-callback-function-in-javascript - William Denniss
4
我认为这个有问题。如果你的额外数据(在本例中是myDiv)在回调函数执行之前发生了改变,你将得到新值而不是旧值!在我的情况下,我正在对数组中的项目发起ajax调用,而第一个success()调用执行时,它认为已经成功处理最后一个项目,但实际上是第一个! bradhouse的答案对我有用。 - Chris
@Chris 是的,在JavaScript中,捕获变量是可以更改的。如果您不希望发生这种情况,则需要创建一个显式函数来捕获值。最常见的习惯用法是自执行函数 (function(myDiv){ ... })(myDiv) 来捕获变量 myDiv。这在 @bradhouse 的回答中隐含地完成了。 - Vincent Robert
如果您调用此ajax n次,则此方法将创建n个不同的函数并将它们传递给成功回调。从性能角度来看,这是不好的。为了避免这种情况,@zeroasterisk的答案是很好的。 - yılmaz
我同意@WilliamDenniss的观点,我认为这是最好的、简单明了的解决方案。 - sebasira

53

其实比大家描述的要简单得多...特别是如果你使用$.ajax({})基本语法而不是其中的helper函数。

当你设置ajax请求时,只需像在任何对象上一样传递key: value对即可... (因为$(this)还没有改变上下文,它仍然是bind调用的触发器)

<script type="text/javascript">
$(".qty input").bind("keypress change", function() {
    $.ajax({
        url: "/order_items/change/"+$(this).attr("data-order-item-id")+"/qty:"+$(this).val()+"/returnas.json",
        type: "POST",
        dataType: "json",
        qty_input: $(this),
        anything_else_i_want_to_pass_in: "foo",
        success: function(json_data, textStatus, jqXHR) {
            /* here is the input, which triggered this AJAX request */
            console.log(this.qty_input);
            /* here is any other parameter you set when initializing the ajax method */
            console.log(this.anything_else_i_want_to_pass_in);
        }
    });
});
</script>

使用这种方法比设置变量更好的原因之一是,变量是全局的,因此可以被覆盖... 如果您有两个可以触发ajax调用的东西,您理论上可以比ajax调用响应更快地触发它们,并且您将第二个调用的值传递给第一个。 使用上面的方法,这种情况不会发生(而且这种方法也很简单)。


3
这正是我正在寻找的。我不知道当你将dataType指定为json时,它会自动解析响应。太好了 (: - Rooster
1
对我来说,这似乎是向回调函数传递额外参数的最佳方法。如果有人发现这种方法有什么缺点,我会很感兴趣。这是最简单、最优雅的方法。谢谢! - Jan Zyka
“the var is global and as such, overwritable”这句话意味着如果你在函数内声明变量,它就不再是全局的,也就不会被覆盖。同时,这种方式比上面的解决方案更易读。使用“闭包”是一种更加简洁的方法-请参见我在此处的答案:https://dev59.com/13NA5IYBdhLWcg3wfd8m#18570951 - Lorenzo Polidori
5
我不同意,我认为采用额外参数的方法更易于阅读。 - Andrew Plummer
2
这真是太棒了。我以前怎么没看到过这个。我不需要闭包,我只需要它能工作,而且这绝对是一流的,干净、简单、非全局的。 - Piotr Kula
真是一个非常好的解决方案! - serraosays

21

在当今世界上,有另一种更为简洁的答案,取自另一个Stack Overflow回答:

function clicked()
{
    var myDiv = $( "#my-div" );

    $.post( "someurl.php", {"someData": someData}, $.proxy(doSomething, myDiv), "json" );
}

function doSomething( data )
{
    // this will be equal to myDiv now. Thanks to jQuery.proxy().
    var $myDiv = this;

    // doing stuff.
    ...
}

以下是原始问题和答案:

jQuery如何将额外的参数传递给$.ajax调用的成功回调函数?

8
您也可以尝试以下内容:

您可以尝试以下方法:

function clicked() {

    var myDiv = $("#my-div");

    $.post("someurl.php",someData,function(data){
        doSomething(data, myDiv);
    },"json"); 
}

function doSomething(curData, curDiv) {

}

5
您可以使用JavaScript闭包:
function wrapper( var1, var2,....) // put here your variables
{
  return function( data, status)
  {
     //Handle here results of call
  }
};

当你能够做到以下操作时:

$.post("someurl.php",data,wrapper(var1, var2, etc...),"html");

4

在我上一篇帖子中犯了一个错误。这是如何在回调函数中传递附加参数的工作示例:

function custom_func(p1,p2) {
    $.post(AJAX_FILE_PATH,{op:'dosomething',p1:p1},
        function(data){
            return function(){
                alert(data);
                alert(p2);
            }(data,p2)
        }
    );
    return false;
}

1
在添加新答案之前,您应该编辑原始答案或将其删除。 - Blazemonger

3

让我们简单点!:)

$.ajax({
    url: myUrl,
    context: $this, // $this == Current $element
    success: function(data) {
        $.proxy(publicMethods.update, this)(data); // this == Current $element
    }
});

2
如果有人还在这里,这是我的看法:
$('.selector').click(myCallbackFunction.bind({var1: 'hello', var2: 'world'}));

function myCallbackFunction(event) {
    var passedArg1 = this.var1,
        passedArg2 = this.var2
}

在绑定回调函数之后,this 将会在函数内部作为可用变量。这个想法来自于 React 如何使用 bind 功能。

2

使用.ajax() jQuery API和闭包传递附加参数到回调函数的更通用的异步请求发送解决方案:

function sendRequest(method, url, content, callback) {
    // additional data for the callback
    var request = {
        method: method,
        url: url
    };

    $.ajax({
        type: method,
        url: url,
        data: content
     }).done(function(data, status, xhr) {
        if (callback) callback(xhr.status, data, request);
     }).fail(function(xhr, status) {
        if (callback) callback(xhr.status, xhr.response, request);
     });
};

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