嚴格來說,這不能算是一篇微信小程式教程,不過會使用到上一篇中的app.js程式碼作為示例,姑且充個數吧。
回撥函式
回撥函式,對於初入程式設計這一行的同學可能會有些難以理解,畢竟回撥函式的使用和程式順序執行的直觀流程是相悖的。
想象你定了一個外賣,一種是你定時去檢視外賣有沒有到,一種是你出示電話號碼給外賣員,到達的時候電話通知你。
很容易可以看出第二種是更加高效的方案,其實這種通知機制應用到程式設計領域,就是回撥函式了。
熟悉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變數,語法上便捷了許多。