刚刚看到有篇文章谈回调过程,我觉得有点长。所以我来谈谈我对回调的理解,以及以java/javascript为代表的回调差异。
回调的概念
当一个函数(foo)执行到特定时机时,命令某个参数(callback)所代表的执行过程去继续执行。
显然,这个callback方法是在外部预先定义好的,这样,调用者就可以灵活制订后期执行策略而不必修改函数(foo)定义。这在类库设计中尤其有用。
先看一个示例:
//定义函数
function foo(p1, p2, callback){
//do something
callback(someArgs);
return true;
}
//回调函数定义
function someCallBack(args){
//do something
}
//回调函数2定义
function someCallBack2(){
//do something
}
//调用函数
foo(pp1, pp2, someCallBack);
foo(pp1, pp2, someCallBack2);
此示例定义了一个包含回调的函数foo,然后定义了两个回调函数(someCallBack, someCallBack2)。最后调用他们,并分别使用不同的回调函数。因此,回调函数可以用来执行最后的过程,并且可以作为参数传入主函数,令主函数的编写可模式化。
回调具备什么特质?
- 必须是个执行过程。
- 可以接受参数,当然也可以不接受。
- 能被定义函数轻易调用执行之。
怎么定义回调?
回调形参作为一个指针在定义函数(foo)中被使用。
调用函数在设定实参时可以直接使用函数或实例的引用,也可使用匿名函数、匿名类、匿名接口、对象实例。
java和javascript的实现有什么差别?
java的回调形参只能是一个类(或者接口)。
原因有二:
- 一是java方法的参数必须是类;
- 二是没有办法传入函数指针,也没有办法直接执行这个函数指针(更不要说将参数带入)。
java不能简单传入一个方法
在javascript中,函数本质上是一个函数对象,任何对它的引用其实都跟java对类实例的引用一样。但不一样的是函数既可作为类存储类变量和类方法,也可直接作为一个执行过程,输入参数就给结果。顺便说一句:作为类时,其内部的类变量保持和类方法能成功执行就必须依靠闭包概念。java中类和类方法不可能使用同一概念,二者必须区别开来,而且类方法的执行必须在对象名的限定之下方可。
为什么要使用回调?
模块化
前面谈到“在类库设计时尤其有用”。的确,定义回调将是模块化代码的好办法。没人希望模块封装良好,经历无数测试用例的代码被反复修改。
在异步代码中,需要有序执行一些过程。
比如上传图片时,一边希望上传过程放到后台进程,UI操作不被阻塞;同时又希望上传完毕后能告诉一下用户,最好能自动刷新一下UI。这时上传就需要放到一段异步代码中,最后告知用户和刷新UI的事就放到回调中执行。这时候回调就用上了。回调就是异步吗?
答案是个大大的NO。在回调执行过程中,foo函数最后那句return true还等在那里的呢。而且调用函数其实还并未结束,在调用栈中还存在着很明确的上下调用的关系。