关于回调函数,匿名函数,闭包的杂谈
回调函数,对于初入编程这一行的同学可能会有些难以理解,毕竟回调函数的使用和程序顺序执行的直观流程是相悖的。
想象你定了一个外卖,一种是你定时去查看外卖有没有到,一种是你出示电话号码给外卖员,到达的时候电话通知你。
很容易可以看出第二种是更加高效的方案,其实这种通知机制应用到编程领域,就是回调函数了。
熟悉win32开发的同学应该知道,典型的windows程序框架就是一个消息循环外加一个窗口过程函数。其中windows系统接管消息接受,之后调用开发者的窗口过程函数来完成具体的消息处理逻辑。窗口过程函数就是一个回调函数。
为什么需要回调函数
以上面的 win32程序为例。我们知道出于安全性考虑,windows操作系统是不允许开发者直接访问硬件资源的。微软的开发者提供了api来处理消息循环,但是具体消息的响应逻辑需要开发者来提供,这种情形下,回调函数就是很好的实现方案。
再举一个例子,想象你参与一个手机设备管理软件项目的开发工作,你负责底层设备通信模块。当用户插入设备到电脑中时,你需要通知上层的模块进行处理。出于灵活性和通用性的考虑,你不可能将设备连接时的处理逻辑放在你负责的模块中,此时可以由上层调用者提供一个回调函数,在设备连接时你的模块调用回调函数即可。
关于回调函数,有一个所谓的好莱坞准则:Don't call me; I'll call you!
匿名函数
在c,c++等语言中,当需要使用回调函数时,需要预先定义一个函数体。而回调函数通常只是提供给其它模块进行调用,为了简化编码,后续的javascript等脚本语言中提供了对匿名函数的支持。(注: 新的c++标准也开始支持匿名函数,称为Lambda函数)
getUserInfo:function(cb){ var that = this if(this.globalData.userInfo){ typeof cb == "function" && cb(this.globalData.userInfo) }else{ //调用登录接口 wx.login({ success: function () { wx.getUserInfo({ success: function (res) { that.globalData.userInfo = res.userInfo typeof cb == "function" && cb(that.globalData.userInfo) } }) } }) } },
上面的代码来自于上一篇教程中的app.js,在调用wx.login时,传递了一个匿名函数进行调用成功后的逻辑处理,就是success后面的部分。可以看到这里只有函数定义而没有函数名称,因此除了作为回调函数外,也无法在其它地方调用该函数。
实际上匿名函数仅仅是一种编码简化而已,不过它带来的好处却不仅仅是减少编码而已。
闭包
在编程技术中,闭包应该属于较高级的技术了。当使用回调函数时,通常会涉及到一些上下文的传递。在c/c++等语言中,会使用全局变量或堆内存来传递上下文。全局变量的缺点很明显,而堆内存又很容易发生内存泄漏。而在更高级的脚本语言中,可以通过闭包技术来轻松的完成上下文传递。
以上面的代码为例,在回调函数中执行了that.globalData.userInfo = res.userInfo来保存用户信息,其中that变量由var that = this赋值,因此该变量指向app对象本身,所以才能成功保存用户信息。
我们可以看到that对象是getUserInfo方法栈上的变量,如果没有闭包技术,此处的匿名回调函数是不能直接使用that变量的,就需要将app对象传递给回调函数(全局变量或函数参数的方式),而在闭包技术的支持下,回调函数可以像使用函数内部变量一样来访问that变量,语法上便捷了许多。
上一篇: HTTP状态码汇总
下一篇: 详解Python中列表和元祖的使用方法