- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) > web開(kāi)發(fā) > JavaScript >
- 關(guān)于JavaScript回調函數的深入理解
JavaScript回調函數是成為一名成功的 JavaScript 開(kāi)發(fā)人員必須要了解的一個(gè)重要概念。但是我相信,在閱讀本文之后,你將能夠克服以前使用回調方法遇到的所有障礙。
在開(kāi)始之前,首先要確保我們對函數的理解是扎實(shí)的。
函數是在其中有一組代碼的邏輯構件,用來(lái)執行特定任務(wù)。實(shí)際上為了易于調試和維護,函數允許以更有組織的方式去編寫(xiě)代碼。函數還允許代碼重用。
你只需定義一次函數,然后在需要時(shí)去調用它,而不必一次又一次地編寫(xiě)相同的代碼。
現在,讓我們看看如何在 javascript 中聲明一個(gè)函數。
在函數體中,函數必須將一個(gè)值返回給調用方。遇到 return 語(yǔ)句后,該函數將會(huì )停止執行。在函數內部,參數將會(huì )充當局部變量。
同樣,在函數內部聲明的變量是該函數的局部變量。局部變量只能在該函數內訪(fǎng)問(wèn),因此具有相同名稱(chēng)的變量可以輕松地用于不同的函數。
在下列任何一種情況下,將調用之前聲明的函數:
() 運算符調用該函數。
按照 MDN 的描述:回調函數是作為參數傳給另一個(gè)函數的函數,然后通過(guò)在外部函數內部調用該回調函數以完成某種操作。
讓我用人話(huà)解釋一下,回調函數是一個(gè)函數,將會(huì )在另一個(gè)函數完成執行后立即執行?;卣{函數是一個(gè)作為參數傳給另一個(gè) JavaScript 函數的函數。這個(gè)回調函數會(huì )在傳給的函數內部執行。
在 JavaScript 中函數被看作是一類(lèi)對象。對于一類(lèi)對象,我們的意思是指數字、函數或變量可以與語(yǔ)言中的其他實(shí)體相同。作為一類(lèi)對象,可以將函數作為變量傳給其他函數,也可以從其他函數中返回這些函數。
可以執行這種操作的函數被稱(chēng)為高階函數?;卣{函數實(shí)際上是一種模式?!澳J健币辉~表示解決軟件開(kāi)發(fā)中常見(jiàn)問(wèn)題的某種行之有效的方法。最好將回調函數作為回調模式去使用。
客戶(hù)端 JavaScript 在瀏覽器中運行,并且瀏覽器的主進(jìn)程是單線(xiàn)程事件循環(huán)。如果我們嘗試在單線(xiàn)程事件循環(huán)中執行長(cháng)時(shí)間運行的操作,則會(huì )阻止該過(guò)程。從技術(shù)上講這是不好的,因為過(guò)程在等待操作完成時(shí)會(huì )停止處理其他事件。
例如,alert 語(yǔ)句被視為瀏覽器中 javascript 中的阻止代碼之一。如果運行 alert,則在關(guān)閉 alert 對話(huà)框窗口之前,你將無(wú)法在瀏覽器中進(jìn)行任何交互。為了防止阻塞長(cháng)時(shí)間運行的操作,我們使用了回調。
讓我們深入研究一下,以便使你準確了解在哪種情況下使用回調。
獲取并顯示消息的函數
在上面的代碼片段中,首先執行 getMessage()函數,然后執行 displayMessage() 。兩者都在瀏覽器的控制臺窗口中顯示了一條消息,并且都立即執行。
在某些情況下,一些代碼不會(huì )立即執行。例如,如果我們假設 getMessage() 函數執行 API 調用,則必須將請求發(fā)送到服務(wù)器并等待響應。這時(shí)我們應該如何處理呢?
我認為與其告訴你 JavaScript 回調函數的語(yǔ)法,不如在前面的例子中實(shí)現回調函數更好。修改后的代碼段顯示在下面的截圖中。
用回調函數顯示消息
為了使用回調函數,我們需要執行某種無(wú)法立即顯示結果的任務(wù)。為了模擬這種行為,我們用 JavaScript 的 setTimeout() 函數。該函數會(huì )暫停兩秒鐘,然后在控制臺窗口中顯示消息“ Hi,there”。
“顯示的消息”將被顯示在瀏覽器的控制臺窗口中。在這種情況下,首先,我們需要等待 getMessage() 函數。成功執行此函數后,再執行 displayMessage() 函數。
讓我解釋一下前面的例子在幕后發(fā)生的事。
從上一個(gè)例子可以看到,在 getMessage() 函數中,我們傳遞了兩個(gè)參數。第一個(gè)參數是 msg 變量,該變量顯示在瀏覽器的控制臺窗口中,第二個(gè)參數是回調函數。
現在,你可能想知道為什么將回調函數作為參數進(jìn)行傳遞 —— 要實(shí)現回調函數,我們必須將一個(gè)函數作為參數傳給另一個(gè)函數。
在 getMessage() 完成任務(wù)后,我們將調用回調函數。之后,當調用 getMessage() 函數時(shí),將引用傳給displayMessage() 函數,該函數就是回調函數。
注意,當調用 getMessage() 函數時(shí),我們僅將其引用傳給 displayMessage() 函數。這就是為什么你不會(huì )在它旁邊看到函數調用運算符,也就是() 符號。
JavaScript 被認為是單線(xiàn)程腳本語(yǔ)言。單線(xiàn)程是指 JavaScript 一次執行一個(gè)代碼塊。當 JavaScript 忙于執行一個(gè)塊時(shí),它不可能移到下一個(gè)塊。
換句話(huà)說(shuō),我們可以認為 JavaScript 代碼本質(zhì)上總是阻塞的。但是這種阻塞性使我們無(wú)法在某些情況下編寫(xiě)代碼,因為在這些情況下我們沒(méi)有辦法在執行某些特定任務(wù)后立即得到結果。
我談?wù)摰娜蝿?wù)包括以下情況:
為了處理這些情況,必須編寫(xiě)異步代碼,而回調函數是處理這些情況的一種方法。所以從本質(zhì)上上說(shuō),回調函數是異步的。
當多個(gè)異步函數一個(gè)接一個(gè)地執行時(shí),會(huì )產(chǎn)生回調地獄。它也被稱(chēng)為厄運金字塔。
假設你要獲取所有 Github 用戶(hù)的列表。然后在用戶(hù)中搜索 JavaScript 庫的主要貢獻者。再然后,你想要在用戶(hù)中獲取姓名為 John 的人員的詳細信息。
為了在回調的幫助下實(shí)現這個(gè)功能,代碼應該如下所示:
http.get('https://api.github.com/users', function(users) { /* Display all users */ console.log(users); http.get('https://api.github.com/repos/javascript/contributors?q=contributions&order=desc', function(contributors) { /* Display all top contributors */ console.log(contributors); http.get('https://api.github.com/users/Jhon', function(userData) { /* Display user with username 'Jhon' */ console.log(userData); }); }); });
從上面的代碼片段中,你可以看到代碼變得更加難以理解,以及難以維護和修改。這是由回調函數的嵌套而引發(fā)的。
可以使用多種技術(shù)來(lái)避免回調地獄,如下所示。
讓我們談?wù)勗鯓佑?async.js 庫避免回調地獄。
根據 async.js 官方網(wǎng)站的描述:Async 是一個(gè)工具模塊,它提供了直接、強大的函數來(lái)使用異步 JavaScript。
Async.js 總共提供約 70 個(gè)函數?,F在,我們將僅討論其中兩個(gè),即 async.waterfall() 和 async.series()。
當你要一個(gè)接一個(gè)地運行某些任務(wù),然后將結果從上一個(gè)任務(wù)傳到下一個(gè)任務(wù)時(shí),這個(gè)函數非常有用。它需要一個(gè)函數“任務(wù)”數組和一個(gè)最終的“回調”函數,它會(huì )在“任務(wù)”數組中所有的函數完成后,或者用錯誤對象調用“回調”之后被調用。
var async = require('async'); async.waterfall([ function(callback) { /* Here, the first argument value is null, it indicates that the next function will be executed from the array of functions. If the value was true or any string then final callback function will be executed, other remaining functions in the array will not be executed. */ callback(null, 'one', 'two'); }, function(param1, param2, callback) { // param1 now equals 'one' and param2 now equals 'two' callback(null, 'three'); }, function(param1, callback) { // param1 now equals 'three' callback(null, 'done'); } ], function (err, result) { /* This is the final callback function. result now equals 'done' */ });
當你要運行一個(gè)函數然后在所有函數成功執行后需要獲取結果時(shí),它很有用。 async.waterfall() 和 async.series() 之間的主要區別在于, async.series() 不會(huì )將數據從一個(gè)函數傳遞到另一個(gè)函數。
async.series([ function(callback) { // do some stuff ... callback(null, 'one'); }, function(callback) { // do some more stuff ... callback(null, 'two'); } ], // optional callback function(err, results) { // results is now equal to ['one', 'two'] });
用技術(shù)術(shù)語(yǔ)來(lái)說(shuō),閉包是捆綁在一起的函數的組合,引用了其周?chē)臓顟B(tài)。
簡(jiǎn)而言之,閉包允許從內部函數訪(fǎng)問(wèn)外部函數的作用域。
要使用閉包,我們需要在一個(gè)函數內部定義另一個(gè)函數。然后,我們需要將其返回或傳給另一個(gè)函數。
從概念上講,回調類(lèi)似于閉包?;卣{基本上是把一個(gè)函數作為另一個(gè)函數的用法。
希望本文能消除你對 javascript 回調函數的所有疑問(wèn)。如果你覺(jué)得這篇文章有幫助,請與他人分享。
到此這篇關(guān)于關(guān)于JavaScript回調函數的文章就介紹到這了,更多相關(guān)JavaScript回調函數內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站