代码之家  ›  专栏  ›  技术社区  ›  Alex

javascript函数作为另一个函数的参数?

  •  11
  • Alex  · 技术社区  · 14 年前

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

    我的问题是:

    您希望何时让您的javascript函数接受另一个函数作为参数?为什么不给函数的返回值分配一个变量,然后像这样把这个变量传递给函数:

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

    我不明白为什么我会选择做第二个例子中的事情。

    你为什么要这样做?什么是一些用例?

    谢谢!!

    7 回复  |  直到 14 年前
        1
  •  24
  •   Orion Edwards    14 年前

    这有几个用例:

    1。wrapper”函数。

    假设你有一堆不同的代码。在每一段代码之前和之后,您还需要做一些其他的事情(例如:日志或Try/Catch异常)。

    您可以编写一个“包装器”函数来处理这个问题。如:

    function putYourHeadInTheSand(otherFunc) {
        try{
             otherFunc();
        } catch(e) { } // ignore the error
    }
    
    ....
    
    putYourHeadInTheSand(function(){
        // do something here
    });
    putYourHeadInTheSand(function(){
        // do something else
    });
    

    2。回调。

    假设您以某种方式加载了一些数据。与其锁定等待加载的系统,不如在后台加载它,并在结果到达时对其进行处理。

    现在你怎么知道它什么时候到达?您可以使用一些类似于信号或互斥体的东西,这些东西很难写并且很难看,或者您可以只创建一个回调函数。您可以将此回调传递给加载程序函数,加载程序函数可以在完成后调用它。

    每次你做一个 XmlHttpRequest ,这几乎就是正在发生的事情。下面是一个例子。

    function loadStuff(callback) {
        // Go off and make an XHR or a web worker or somehow generate some data
        var data = ...;
        callback(data);
    }
    
    loadStuff(function(data){
        alert('Now we have the data');
    });
    

    三。生成器/迭代器

    这与回调类似,但您可以多次调用回调,而不是只调用一次回调。假设您的加载数据函数不只是加载一个数据位,也许它加载200个。

    这最终与for/foreach循环非常相似,只是它是异步的。(您不必等待数据,它会在数据准备就绪时调用您)。

    function forEachData(callback) {
        // generate some data in the background with an XHR or web worker
        callback(data1);
        // generate some more data in the background with an XHR or web worker
        callback(data2);
        //... etc
    }
    
    forEachData(function(data){
        alert('Now we have the data'); // this will happen 2 times with different data each time
    });
    

    4。延迟加载

    假设您的函数对某些文本执行了某些操作。但它只需要文本,可能是5次中的一次,而且加载文本可能非常昂贵。

    所以代码看起来像这样

    var text = "dsakjlfdsafds"; // imagine we had to calculate lots of expensive things to get this.
    var result = processingFunction(text);
    

    处理功能实际上只需要文本20%的时间!我们浪费了所有的努力,把多余的时间装进去。

    您可以传递生成文本的函数,而不是传递文本,如下所示:

    var textLoader = function(){ return "dsakjlfdsafds"; }// imagine we had to calculate lots of expensive things to get this.
    var result = processingFunction(textLoader);
    

    你必须改变你的 processingFunction 期望另一个函数而不是文本,但这确实很小。现在发生的是 处理函数 只会打电话给 textLoader 它所需要的20%的时间。另外80%的时间,它不会调用函数,您也不会浪费所有的精力。

    4a缓存

    如果你有懒惰的装载,那么 文本加载器 函数一旦得到结果文本,就可以将其私有存储在变量中。第二次有人打电话给 文本加载器 它可以返回这个变量,避免昂贵的计算工作。

    调用的代码 文本加载器 不知道也不关心数据是缓存的,它透明地更快。

    通过传递函数,你可以做很多更高级的事情,这只是表面上的划痕,但希望它能为你指明正确的方向。

        2
  •  7
  •   Jon Benedicto    14 年前

    最常见的用法之一是作为回调。例如,取一个对数组中的每个项运行函数的函数,并将结果重新分配给数组项。这要求函数为每个项调用用户的函数,这是不可能的,除非它有传递给它的函数。

    以下是此类函数的代码:

    function map(arr, func) {
        for (var i = 0; i < arr.length; ++i) {
            arr[i] = func(arr[i]);
        }
    }
    

    使用的一个例子是将数组中的每个项乘以2:

    var numbers = [1, 2, 3, 4, 5];
    map(numbers, function(v) {
        return v * 2;
    });
    
    // numbers now contains 2, 4, 6, 8, 10
    
        3
  •  5
  •   SLaks    14 年前

    如果 callerFunction 想打电话 doStuff 稍后,或者如果它想多次调用它。

    这种用法的典型示例是回调函数,在该函数中将回调传递给类似 jQuery.ajax ,然后在完成某些操作(如Ajax请求)时调用回调。

    编辑 :要回答您的意见:

    function callFiveTimes(func) {
        for(var i = 0; i < 5; i++) {
            func(i);
        }
    }
    
    callFiveTimes(alert);  //Alerts numbers 0 through 4
    
        4
  •  2
  •   Benson    14 年前

    在许多情况下,将函数作为参数传递给另一个函数是有用的。最简单的是一个函数 setTimeout ,它需要一个函数和一个时间,并且在该时间之后将执行该函数。如果您以后想做点什么,这很有用。显然,如果调用函数本身并将结果传递给 设置超时 函数,它可能已经发生,以后不会发生。

    另一种情况是,当您想在执行一些代码块之前和之后进行某种设置和拆卸时,这是很好的。最近我遇到了一种情况,我需要销毁jquery ui手风琴,做一些事情,然后重新创建手风琴。我需要做的事情有很多不同的形式,所以我写了一个函数 doWithoutAccordion(stuffToDo) . 我可以传递一个在拆卸和手风琴设置之间执行的函数。

        5
  •  0
  •   josh3736    14 年前

    回调。假设您正在执行异步操作,比如Ajax调用。

    doSomeAjaxCall(callbackFunc);
    

    在dosomeAjaxCall()中,您将回调存储到变量,例如 var ajaxCallback 然后当服务器返回结果时,可以调用回调函数来处理结果:

    ajaxCallback();
    
        6
  •  0
  •   Personman    14 年前

    作为一个Web程序员,这可能不会有太多实际的用途,但是作为一级对象的函数还有另一类用途,目前还没有出现。在大多数函数语言中,比如scheme和haskell,将函数作为参数传递,连同递归一起,是编程的主要部分,而不是偶尔使用的部分。高阶函数(对函数进行操作的函数)如map和fold可以实现在命令式语言中不那么容易获得的非常强大、表达性和可读性的习语。

    map是一个函数,它获取一个数据列表和一个函数,并返回一个通过依次将该函数应用于列表中的每个元素而创建的列表。所以如果我想更新我的弹跳球模拟器中所有弹跳球的位置,而不是

    for(ball : ball_list) {
       ball.update();
       ball.display();       
    }
    

    我改写(在计划中)

    (display (map update ball-list))
    

    或者在Python中,它提供了一些更高阶的函数和更熟悉的语法,

    display( map(update, ball-list) )
    

    fold接受一个两位函数、一个默认值和一个列表,并将该函数应用于默认元素和第一个元素,然后应用于该元素和第二个元素的结果,依此类推,最后返回返回返回的最后一个值。因此,如果我的服务器正在批量发送帐户事务,而不是写入

    for(transaction t : batch) {
        account_balance += t;
    }
    

    我会写

    (fold + (current-account-balance) batch))
    

    这些只是最常见的霍夫函数的最简单用法。

        7
  •  0
  •   gvaish    14 年前

    我将用排序方案来说明这一点。

    假设您有一个对象来代表公司的员工。员工具有多个属性-ID、年龄、工资、工作经验等。

    现在,您要对员工列表进行排序——一种情况是按员工ID排序,另一种情况是按工资排序,另一种情况是按年龄排序。

    现在你唯一想要改变的就是如何比较。

    因此,不必使用多个排序方法,您可以使用一个排序方法,该方法引用可以进行比较的函数。

    示例代码:

    function compareByID(l, r) { return l.id - r.id; }
    function compareByAge(l, r) { return l.age - r.age; }
    function compareByEx(l, r) { return l.ex - r.ex; }
    
    function sort(emps, cmpFn) {
       //loop over emps
       // assuming i and j are indices for comparision
        if(cmpFn(emps[i], emps[j]) < 0) { swap(emps, i, j); }
    }