1、概念
-
在js中,不限制入参类型
-
函数类型的入参就被称之为回调函数
-
在定义函数时,回调函数一般被放在最后一个参数,但不强制
-
官方说法:CALLBACK,即回调函数,是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
2、代码示例
①、最简单回调函数
function addPrintResult(a,b,printResult){ let result = a + b; printResult(result); } function printResult(result){ console.log(result); } addPrintResult(1,2,printResult); 运行结果: 3
②、匿名式回调函数
function addPrintResult(a,b,callback){ let result = a + b; callback(result); } addPrintResult(1,2,function (result) { console.log(result); }); 运行结果: 3
用法解析(重要):
-
定义addPrintResult函数时,共声明了3个入参,a,b,callback,此时js并不知道哪个是回调函数,因为js变量不是强类型的,只有代码编写者自己知道
-
无论如何,反正在实际代码中,第三个参数-callback直接当做 function 来使了!
-
而且,callback这个入参,在实际调用中,真的是个函数, 只不过是没有自己的名字而已,单纯一个函数对象
-
函数,也是个对象,function(){} 和 {name:'wugb'},没什么区别
-
最终,这个匿名函数对象在addPrintResult方法内被赋值给了callback变量,然后callback执行成功
-
在js解释器中的代码应当如下:
let result = a + b; callback = function (result) { console.log(result); } callback(result); 运行结果: 3
-
匿名回调函数应当用到了 var 变量名 = function(){};的类似用法
3、回调函数需注意点
①、要确保实参一定是函数!
function addPrintResult(a,b,callback){ let result = a + b; callback(result); } addPrintResult(1,2,'第三个参数'); 运行结果: TypeError: callback is not a function
-
瞎鸡儿填写实参,代码运行时肯定报错
②、方法内检验回调函数参数类型!
function addPrintResult(a,b,callback){ let result = a + b; if(typeof(callback) == "function"){ callback(result); }else{ console.log('callback 就TM不是个函数,没法回调。') } } addPrintResult(1,2,'第三个参数'); 运行结果: callback 就TM不是个函数,没法回调。
4、和java中的区别
-
直接将函数对象当成实参的用法,java中没有
-
java调用方法时,只能传递A方法的返回值给B方法,而不能将A方法体作为实参传给B方法
-
如果需要A方法调用B,那么写法都是这个样子
function addPrintResult(a,b){ let result = a + b; callback(result); } function callback(result){ console.log(result); } addPrintResult(1,2);
-
不太清楚新的lambda语法可不可以,后面再补充
-
java是强类型语言,就算有回调函数,估计也得是这种类型(伪代码)
function addPrintResult(int a,int b,function callback){ let result = a + b; callback(result); } addPrintResult(1,2,'第三个参数');//写的时候直接报错,因为第三个参数不是function类型
-
是不是TypeScript就有上面这种类似写法了?学ts的时候回头再补充这块
5、回调函数的应用场景
①、ajax异步通讯结果的处理
funtion ajaxReq(url,callback){ let retData = req(url); callback(retData); }
②、连续两次ajax请求(重要)
-
ajax一般是异步请求,所以不能同步获取到第一次请求的响应数据
-
下面的代码不考虑同步响应的场景
-
第二次ajax请求需要用到第一次ajax返回结果的场景
funtion ajaxReq(url,callback){ let retData = req(url); callback(retData); } funtion firstReqFunc(url1){ ajaxReq(url1,function(retData){ let firstRetData = retData; console.log('第一次请求获取到了结果!'+firstRetData); }) } funtion secondReqFunc(firstRetData,url2){ ajaxReq(url2,function(retData){ let secondRetData = retData; console.log('第二次请求获取到了结果!'+secondRetData); dealWithData(firstRetData,secondRetData); }) }
调用1:
注意!!直接这么写,必不可以,因为是异步方法
let firstRetData = firstReqFunc(url1);//此时firstRetData是拿不到数据的 secondReqFunc(firstRetData,url2);
改造后:
//第一步:改造firstReqFunc方法,给它加个入参,类型为回调函数 funtion firstReqFunc(url1,callback){ ajaxReq(url1,function(retData){ let firstRetData = retData; console.log('第一次请求获取到了结果!'+firstRetData); if(callback){ callback(firstRetData);//回调函数中是肯定能拿到第一次请求返回值的,并且该回调函数会被调用 } }) } //第二步:在回调函数中调用第二个请求方法 function getFirstDataAndReqSecondReq(firstData){ secondReqFunc(firstData,url2); } //实际用法为: firstReqFunc(url1,getFirstDataAndReqSecondReq); /* 1、第一次请求获取到了结果 2、将结果传递给了回调函数 3、回调函数被调用时有了值,然后去调用第二次请求 */
6、为什么要用回调函数
-
因为灵活
-
大大降低代码耦合
-
以连续两次ajax请求为例,如果不用回调函数,想要第二次请求时就有第一次请求结果,那就只这么做
-
在第一次请求方法的ajax回调函数中直接调用第二次请求方法
-
-
如果第一次请求方法很多地方都在用,而第二次请求方法只在其中一个场景下用到呢?直接写死岂不是会影响到其它场景
-
而且直接写死的话,维护及其困难,如果新场景是第一次请求后,把响应数据交给第三次请求方法使用呢?又得改老代码,而用到回调函数的话,只需要定义新的回调函数就行了,不影响之前的
-
综上所述:要在合适的地方,尽量灵活使用回调函数
7、其它使用场景
以后每有用到的地方再补充吧,感觉应该有很多地方可以用到