回调(callback)指的是某函数在执行的时候,由于某些原因,该函数被迫中止执行,交出当前线程的使用权,当前线程转而执行其它函数的过程。因为一般等这个函数执行完毕时,当前线程的使用权会返回到之前中止执行的那个函数中,所以将这个过程称为回调。
回调中的“调”,指的是那个被迫中止执行的函数对“其它函数”的调用。回调中的“回”,指的是那个“其它函数”以后将会将当前线程使用权返回给那个被迫中止执行的函数。这个“其它函数”往往不是自己编写的函数,而是源自于外源注入。
回调函数指的即为上述“回调”中的“其它函数”。回调函数一般是经过事先设定(传入),等到某事件发生时就被触发的函数。比方说,事件监听器中设定的函数等等。
有些时候,如果回调函数执行时间很短,有返回值且能控制程序的走向,那么也把它称之为 钩子
(hook)。
在有些编程语言中,如果允许回调函数使用传入处的局部变量,那么也把它称之为 闭包
(closure)。
回调函数与普通函数的区别在于,一般的函数是自己负责编写实现,自己负责调用。而回调函数是自己负责编写实现,但自己不负责调用。自己只是把函数具体实现编写好,然后交给别人来调用。
另一个区别是,(为了方便,这里将调用回调函数或普通函数的函数称之为主函数)。主函数在调用普通函数时,数据的传输流程一般为,先从普通函数调用时的实参传入,然后到达普通函数的形参,最后由普通函数的返回值到达主函数。对不同的编程语言,有些时候,普通函数也可以通过传入的实参、抛出的异常来将结果反馈给主函数。
但主函数使用回调函数时,这个过程描述起来就比较复杂了。回调函数通常是异步调用的,因为在主函数传入回调函数时,并没有马上执行该函数,而是改为异步执行,因此回调函数向主函数反馈结果时也一般不能像普通函数一样通过返回值来进行同步阻塞式反馈。通常,这同时还使用了一种称之为 闭包
的技术,来使得回调函数在真正执行时,可以修改主函数的数据,来达到来主函数反馈数据的目的。
【附】
什么是闭包:https://blog.csdn.net/wangpaiblog/article/details/113360870
什么是同步与异步、并行与并发、阻塞与挂起:https://blog.csdn.net/wangpaiblog/article/details/116114098
什么是同步阻塞、同步非阻塞、异步阻塞、异步非阻塞:https://blog.csdn.net/wangpaiblog/article/details/117236684
钩子(hook)指的是一个程序预设的子程序跳转入口。在不同的编程语言、不同场景下,钩子可以指预先约定的某一类函数名、某个类的接口引用字段等等。
钩子在生活中指的是可以钩住其它东西的一种工具,只要这个东西上有能被钩子钩住的圆环即可。如果一个工具上带有钩子,我们就可以在不破坏这个工具的前提下用我们自己的工具连接这个工具,这只需要在我们自己的工具上安装一个圆环即可。在编程中,这里的钩子相当于一个第三方的黑盒,圆环相当于一个对方指定的要实现的接口,只要自己编写的程序遵守了对方的规范并实现了这个接口,就可以在不需要知道和改动黑盒内部结构的情况下让该黑盒使用自己。
钩子是 IoC
思想的体现。它们有时候也称之为一种程序里的后门。它们体积不大,运行时间不长,但却能控制程序的走向。它是开发者提供给用户的一个功能强大的工具。
在有些场景中,钩子是一种回调函数(callback)。
广义的钩子指的是一个可以控制程序流程的一段代码。但这段代码与程序其它部分的代码属于低耦合,即可以通过不改动其它部分的代码的条件下,任意地更改这段代码来控制程序的走向。
因此,广义的钩子不仅包括上面介绍的钩子,还包括一些类中预设的布尔函数,比方说,可以对某一系列的类中都设置一个布尔函数,然后用该布尔函数的返回值来决定程序走向,则该布尔函数就是一个广义的钩子。
关于代理、委托,可见笔者的另一篇博客:
代理、委托、打桩的区别:
https://blog.csdn.net/wangpaiblog/article/details/130570779
钩子和代理也有些渊源。在某种角度上,含钩子的类也可以看成是一个代理类。但钩子和代理的区别在于,它们设计思路(流程)正好相反。对于代理,一般来说,被代理的类是很早以前就已经设计好了,而代理类依赖于被代理的类且设计时间晚于被代理类。但对于钩子,含钩子的程序往往是提交已经设计好了,之后只需要在使用该程序之前,将“圆环程序”提供该“钩子程序”,就可以使用该“钩子程序”了。可以看出,虽然“钩子程序”是依赖“圆环程序”的,但“圆环程序”的设计时间与“钩子程序”相对独立,是可以晚于“钩子程序”的。另外,“钩子”并未严格限定“圆环程序”的内容,“圆环程序”只需要遵守某种约定的规范,其内容可以自由发挥。这就是 IoC 思想。如果硬要把代理中的概念与钩子相对应,那么从这个角度上,代理与钩子的区别在于,对于代理,可以自由发挥的是代理类,而对于钩子,则是被代理类。
钩子可以看作是委托的升级版或者是特别版,其耦合度比委托要低。
在下面的这些情况下,应当设计成钩子而不是委托:
程序已经抽象出来一套生命周期
活动的实现与使用之间没有高内聚的要求
需要将接口对第三方暴露
对外暴露的接口的返回值将决定程序的走向
钩子与回调函数这两个词的意思非常接近,只是描述事物的侧重点有些不同,就像等边三角形与等角三角形,因此经常混用。
钩子相对于普通的回调函数,通常有如下特点:
内容短小、操作步骤很基本、耗时小
可以通过其返回值来控制程序走向
一般不会开启新线程或后台任务
回调函数相对于钩子,通常有如下特点:
回调函数由事件触发,且允许反复触发
对于 UI 领域,这种函数往往是 纯函数
,且返回值常常为空(void)
当程序的运行轨迹改变时可以触发它,但它不会主动控制程序走向
关于什么是纯函数,可见笔者的另一篇博客:
什么是纯函数?:
https://blog.csdn.net/wangpaiblog/article/details/114005888
句柄(handle)指的是一种通过它能够访问程序内部,甚至控制程序的某种数值。在不同的编程语言、不同场景下,它可以指的是一种内存地址、指针、类对象等等。
【注意】
句柄的英文是 handle
,而不是 handler
。但 handler
也是一个编程中的术语,它和 handle
完全不是同一个东西。但由于英文语法与中文语法的区别,很难找到一个没有用过的两个字的中文词汇来翻译它。handler
实际上是一种事件处理程序(处理器),英文解释是 callback routine
,因此,它的意思更接近回调函数(callback)或者钩子(hook)。所以,handler
并不是句柄的意思。
钩子是开发者提供给用户的一个功能强大的工具。就功能强大而言,这有点类似于句柄。但从使用方法来说,它们更像是相反的东西。
句柄提供了一些可供调用的 API,而钩子则要求自己实现这些 API 的内容,然后注入到程序中。