2

这些天我学习了很多 javascript,我不太了解的一件事是将函数作为参数传递给其他函数。我得到了做这些事情的概念,但我自己想不出任何理想的情况。

我的问题是:

你什么时候想让你的 javascript 函数将另一个函数作为参数?为什么不将变量分配给该函数的返回值并将该变量传递给函数,如下所示:

// Why not do this
var foo = doStuff(params);
callerFunction(foo);

//instead of this
callerFunction(doStuff);

我很困惑为什么我会选择做第二个例子中的事情。

你为什么要这样做?有哪些用例?

4

10 回答 10

3

处理程序/侦听器就是一个很好的例子。

更一般地,当您还不知道 g 是否需要调用 f、需要调用多少次和/或使用哪些参数时,您可以将函数 f 作为参数传递给函数 g。

例子:

  • 排序算法:比较函数
  • 正则表达式:替换函数
  • 回调(例如事件处理程序)
于 2012-11-06T22:54:48.587 回答
3

这是另一个例子。对数组进行一些格式化操作:

function pctFormatter(num) {
  return num + '%';
}

function centsFormatter(num) {
  return num + '.00';
}

function formatThisArray(array, formatter) {
  var output = [];
  for(var i = 0; i < array.length; i++) {
    output.push( formatter(array[i]) );
  }
  return output;
}

formatThisArray([1,2,3], pctFormatter);// returns ['1%', '2%', '3%']
formatThisArray([1,2,3], centsFormatter);// returns ['1.00', '2.00', '3.00']
于 2012-11-06T22:56:06.917 回答
2

当您没有params通过时,您会这样做,但callerFunction()确实如此。

AJAX 请求的回调是一种用例。

function myCallback(response) {
    // do something with the response
}

myAJAX('http://example.com/foo.json', myCallback)

这允许myAJAX发出请求并等待响应的工作。然后,myCallback当响应最终到达时,它会调用并将响应传递给它。

于 2012-11-06T22:49:28.530 回答
1
// Why not do this
var foo = doStuff(params);
callerFunction(foo);

//instead of this
callerFunction(doStuff);

第一个示例将运行该函数doStuff并将params结果分配给foo. callerFunction将使用参数调用foo(现在是 的结果dooStuff);

第二个示例将调用并作为参数callerFunction传递。doStuff可能会callerFunction或可能不会调用doStuff.

于 2012-11-06T22:52:53.367 回答
1

好吧,有时你不知道函数的调用者是谁,直到它被调用 - 这排除了传递预先计算的值。

浮现在脑海中的几个例子是:

(a) setTimeout 或 setInterval - 您想在指定时间段后调用特定函数,可以是一次性的,也可以是重复的。如果调用的函数返回一个依赖于时间的值,则在某些情况下您可能无法预先计算该值 - 它需要在预定时间完成。因此,我们告诉函数在指定时间调用我们自己的哪些函数。

(b) 在加载(或至少尝试)各种资源时。我们可以给元素一个函数,加载成功时执行,加载失败时执行另一个函数。在调用这两个(用户提供的)函数中的任何一个之前,您实际上并不知道加载资源的工作何时完成。在有许多资源的情况下,您可以在此处增加保持成功/失败加载尝试次数的计数器。

(c) 调用 getElementsByClass 或 getElementsByTagName 返回的 NodeList。它不是一个实际的(javascript 原生)Array 对象。因此,您不能像使用数组一样调用 forEach 方法。为了解决这个问题,我使用以下辅助函数:

// getElementsByTagName, getElementsByClass - both return a NodeList
// it is accessed in the same way as an array - with the [] operators, but it's
// not an array object - this is a function that allows us to still iterate through it
// in much the same way.
function forEachNode(nodeList, func)
{
    var i, n = nodeList.length;
    for (i=0; i<n; i++)
    {
        func(nodeList[i], i, nodeList);
    }
}

这允许我获取节点列表,然后在每个节点上调用一些用户定义的函数。在使用中,它看起来像这样:

var allAnchors = document.getElementsByTagName('a');
forEachNode(allAnchors, showNodeTextVal);
function showNodeTextVal(curElem, curIndex, origList)
{
  alert(curElem.innerText);
}

或者更简单地说:

var allAnchors = document.getElementsByTagName('a');
forEachNode(allAnchors, function(curElem){alert(curElem.innerText);} );

与我们不使用这个辅助函数相比,这是一个更清晰、更不容易出错的情况。为了实现相同的功能,我们需要编写以下代码:

var nodeList = document.getElementsByTagName('a');
var i, n = nodeList.length;
for (i=0; i<n; i++)
{
  alert(nodeList[i].innerText);
}
于 2012-11-06T23:04:36.967 回答
0

最常见的情况是 JQuery 中的处理程序:

function clickHandler(e){
  // handle click on e.Target
}

$("#button").click(clickHandler);

$(function(){
// do ready state initialization
});
于 2012-11-06T22:49:05.770 回答
0
callerFunction(doStuff);

使用此代码,您可以将函数的“指针”指向doStuff函数callerFunction

你可以像这样使用它:

function callerFunction(doStuff) {

   var x = doStuff(...);
   ...;
}

您可以在函数中使用该函数而不仅仅是doStuff.

问候!

于 2012-11-06T22:53:01.813 回答
0

你什么时候想让你的 javascript 函数将另一个函数作为参数?

它对回调很有用,例如:

function add( a, b, callback ) {
  callback( a, b );
  return a + b;
}

function added( a, b ) {
  alert('You just added two numbers: '+ a +' and '+ b);
}

alert( add( 1, 2, added ); // Will alert the message and then the result.

这是一个非常简单的示例,但它对异步函数非常有用,因此您可以在代码完成后运行代码而不会中断脚本。

于 2012-11-06T22:53:55.713 回答
0

当你想让你的代码真正将函数作为函数处理时,你需要传递函数本身,而不是返回值——要执行的代码。考虑这个伪代码示例:

function saveToLocalStorage(data) {...//saves to local storage}

function saveToServer(data) {...//saves via AJAX to server}

function saveToAmazonS3(data) {.../saves to Amazon S3 }

function multiSave(data, saverFunctions) {
    saverFunctions.forEach(function (saverFunction) {
      saverFunction(data);
    });
}

multiSave({user: "tim"}, [saveToLocalStorage, saveToServer, saveToAmazonS3]);

在这种情况下,我希望传递实际函数本身,并让其他代码稍后调用它们。当我们这样做时,诸如此类的函数multiSave被称为高阶函数,因为它直接处理其他函数。由于 multiSave 的工作方式,我可以轻松地在本地/服务器/S3 旁边的 UI 中放置一些复选框,并允许用户以一种不太优雅的方式选择他们的数据去向,如果我无法将函数传递为论据。

于 2012-11-06T22:55:06.337 回答
0

当您将函数作为参数传递时,该参数不是该函数的返回值,而是函数本身,您可以随意调用它,使用任何您喜欢的参数,或者您可以将其分配给一个事件。
你说你想要一些实际的用例,这里有一个非常常见的情况的简短列表,所有情况都需要一个函数作为参数传递。

让我们看看你的平均 jQuery 代码,并计算一个函数作为参数传递的次数:

$(document).ready(function()//<-- 1
{
    $('#foo').on('click',function()//2
    {
    });
    $.each(something,function()//3
    {});
    //and so on
});

如果您不使用 jQuery,请尝试事件委托

document.body.addEventListener('click',function(e)
{
    e = e || window.event
    console.log('This function was passed as an argument to the addEventListener method');
},false);

甚至是简单的Array.prototype.sort函数(/method):

anArray.sort(function(a,b)
{
    return (a > b ? 1 : -1);
});

或者,如果您需要进行 ajax 调用,而不是XMLHttpRequest当场创建新对象,您可能需要一个单独的函数来设置 xhr 对象,并将 url、数据onreadystatechange回调作为参数传递:

function makeXHR(url,data,callback)
{
     try
     {
          var xhr = new XMLHttpRequest();
      }
      catch(e)
      {
           //etc...
      }
      xhr.onreadystatechange = callback;
}
makeXHR('some/url','foo=bar',function()
{
    if (this.readyState === 4 && this.status === 200)
    {
        //do stuff
    }
});

在所有这些示例中,我已经创建了内联函数,当然引用一个函数(仅通过它的名称)也可以正常工作:

makeXHR('some/url','foo=bar',defaultXhrCallback);

这些只是数千个用例中的几个,您可以/必须将函数作为参数传递给另一个函数

于 2012-11-06T22:59:53.217 回答