这些天我学习了很多javascript,其中一个我不太了解的事情是将函数作为参数传递给其他函数。我得到了做这些事情的概念,但我自己无法想出任何理想的情况。
我的问题是:
您希望何时使用其他函数作为参数?为什么不直接为该函数的返回值赋值,并将该变量传递给函数,如下所示:
// Why not do this
var foo = doStuff(params);
callerFunction(foo);
//instead of this
callerFunction(doStuff);
我很困惑为什么我会选择做第二个例子中的事情。
你为什么要这样做?有哪些用例?
谢谢!
答案 0 :(得分:24)
有几个用例:
让我们说你有很多不同的代码。在每一段代码之前和之后,你想要做其他的事情(例如:log,或try / catch异常)。
您可以编写“包装”功能来处理此问题。 EG:
function putYourHeadInTheSand(otherFunc) {
try{
otherFunc();
} catch(e) { } // ignore the error
}
....
putYourHeadInTheSand(function(){
// do something here
});
putYourHeadInTheSand(function(){
// do something else
});
让我们说你以某种方式加载一些数据。您可以在后台加载它,而不是锁定等待加载的系统,并在结果到达时对结果执行某些操作。
现在你怎么知道什么时候到来?你可以使用像信号或互斥的东西,这很难写和丑,或者你可以只做一个回调函数。您可以将此回调传递给Loader函数,该函数可以在完成后调用它。
每次你做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
});
让我们说你的函数对某些文本做了些什么。但它只需要文本中的一次可能是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
以期待另一个功能而不是文本,但这确实很小。现在发生的事情是processingFunction
只会在需要它的20%的时间内调用textLoader
。另外80%的时间,它不会调用该功能,你也不会浪费所有的努力。
如果你发生了延迟加载,那么textLoader
函数可以在结果文本获得后私下将结果文本存储在变量中。第二次有人调用textLoader
时,它可以返回该变量并避免昂贵的计算工作。
调用textLoader
的代码不知道或不关心数据是否被缓存,它透明地更快。
你可以通过传递函数来做更多高级的事情,这只是表面上看,但希望它指出你正确的方向: - )
答案 1 :(得分:7)
最常见的用法之一是作为回调。例如,使用一个函数对数组中的每个项运行一个函数,并将结果重新分配给数组项。这要求函数为每个项调用用户的函数,除非传递函数,否则这是不可能的。
以下是此类功能的代码:
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
答案 2 :(得分:5)
如果callerFunction
想要稍后致电doStuff
,或者想要多次致电,请执行此操作。
这种用法的典型示例是回调函数,您将回调传递给类似jQuery.ajax
的函数,然后在完成某些操作时调用您的回调(例如AJAX请求)
编辑:回答您的评论:
function callFiveTimes(func) {
for(var i = 0; i < 5; i++) {
func(i);
}
}
callFiveTimes(alert); //Alerts numbers 0 through 4
答案 3 :(得分:2)
将函数作为参数传递给另一个函数在许多情况下都很有用。最简单的是像setTimeout
这样的函数,它接受一个函数和一个时间,并且在该时间过去之后将执行该函数。如果您想稍后执行某些操作,这将非常有用。显然,如果你调用函数本身并将结果传递给setTimeout
函数,它就已经发生了,以后就不会发生了。
另一种情况很好,就是当你想在执行某些代码块之前和之后进行某种设置和拆卸时。最近我有一种情况需要销毁一个jQuery UI手风琴,做一些事情,然后重新创建手风琴。我需要做的事情采用了许多不同的形式,所以我写了一个名为doWithoutAccordion(stuffToDo)
的函数。我可以传递一个在拆解和手风琴设置之间执行的功能。
答案 4 :(得分:0)
回调。假设您正在做异步操作,比如AJAX调用。
doSomeAjaxCall(callbackFunc);
在doSomeAjaxCall()中,您将回调存储到变量,如var ajaxCallback
然后当服务器返回其结果时,您可以调用回调函数来处理结果:
ajaxCallback();
答案 5 :(得分:0)
对于作为Web程序员来说,这可能不会有太大的实际用处,但是还有另一类用于将函数作为尚未出现的第一类对象的用法。在大多数函数式语言中,如Scheme和Haskell,将函数作为参数传递,与递归一起,是编程的关键,而不是偶尔使用的东西。高阶函数(对函数进行操作的函数),如map和fold,可以实现非常强大,富有表现力和可读的习语,这些习惯用语不是那么容易获得的。
Map是一个函数,它接收数据和函数列表,并返回通过依次将该函数应用于列表的每个元素而创建的列表。所以,如果我想在我的弹跳球模拟器中更新所有弹跳球的位置,而不是
for(ball : ball_list) {
ball.update();
ball.display();
}
我会写(在Scheme中)
(display (map update ball-list))
或在Python中,它提供了一些高阶函数和更熟悉的语法,
display( map(update, ball-list) )
Fold采用两位函数,默认值和列表,并将函数应用于默认值和第一个元素,然后应用于该结果和第二个元素,依此类推,最后返回最后一个返回值。因此,如果我的服务器是批量发送帐户交易,而不是写
for(transaction t : batch) {
account_balance += t;
}
我会写
(fold + (current-account-balance) batch))
这些只是最常见的HOF的最简单用法。
答案 6 :(得分:0)
我将用排序方案说明。
假设您有一个对象来代表公司的员工。员工有多种属性 - 身份,年龄,工资,工作经验等。
现在,您要对员工列表进行排序 - 在一种情况下按员工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); }
}