亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 語言 > JavaScript > 正文

詳解JavaScript的回調函數

2024-05-06 16:25:11
字體:
來源:轉載
供稿:網友

這篇文章主要介紹了JavaScript的回調函數,幫助大家正確理解和使用 JavaScript中的回調函數,感興趣的小伙伴們可以參考一下

本文的目錄:

什么是回調或高級函數

回調函數是如何實現的

實現回調函數的基本原則

回調地獄的問題和解決方案

實現自己的回調函數

在JavaScrip中,function是內置的類對象,也就是說它是一種類型的對象,可以和其它String、Array、Number、Object類的對象一樣用于內置對象的管理。因為function實際上是一種對象,它可以“存儲在變量中,通過參數傳遞給(別一個)函數(function),在函數內部創建,從函數中返回結果值”。

因為function是內置對象,我們可以將它作為參數傳遞給另一個函數,延遲到函數中執行,甚至執行后將它返回。這是在JavaScript中使用回調函數的精髓。本篇文章的剩余部分將全面學習JavaScript的回調函數。回調函數也許是JavaScript中使用最廣泛的功能性編程技術,也許僅僅一小段JavaScript或jQuery的代碼都會給開發者留下一種神秘感,閱讀這篇文章后,也許會幫你消除這種神秘感。

回調函數來自一種著名的編程范式——函數式編程

,在基本層面上,函數式編程指定的了函數的參數。函數式編程雖然現在的使用范圍變小了,但它一直被“專業的聰明的”程序員看作是一種難懂的技術,以前是這樣,未來也將是如此。

幸運的是,函數式編程已經被闡述的像你我這樣的一般人也能理解和使用。函數式編程最主要的技術之一就是回調函數,你很快會閱讀到,實現回調函數就像傳遞一般的參數變量一樣簡單。這項技術如此的簡單,以至于我都懷疑為什么它經常被包含在JavaScript的高級話題中去。

一、什么是回調或高級函數?

回調函數被認為是一種高級函數,一種被作為參數傳遞給另一個函數(在這稱作"otherFunction")的高級函數,回調函數會在otherFunction內被調用(或執行)?;卣{函數的本質是一種模式(一種解決常見問題的模式),因此回調函數也被稱為回調模式。

思考一下下面這種在jQuery中常用的回調函數:

 

 
  1. //Note that the item in the click method's parameter is a function, not a variable. 
  2. //The item is a callback function 
  3. $("#btn_1").click(function() { 
  4. alert("Btn 1 Clicked"); 
  5. }); 

正如在前面的例子所看到的,我們傳遞了一個函數給click方法的形參,click方法將會調用(或執行)我們傳遞給它的回調函數。這個例子就給出了JavaScript中使用回調函數的一個典型方式,并廣泛應用于jQuery中。

細細體味一下另一個基本JavaScript的典型例子:

 

 
  1. var friends = ["Mike""Stacy""Andy""Rick"]; 
  2.  
  3. friends.forEach(function (eachName, index){ 
  4. console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick 
  5. }); 

我們再一次用同樣的方式傳遞了一個匿名的函數(沒有函數名的函數)給forEach方法,作為forEach的參數。

到目前為止,我們傳遞了一個匿名的函數作為參數給另一個函數或方法。在看其它更復雜的回調函數之前,讓我們理解一下回調的工作原理并實現一個自己的回調函數。

二、回調函數是如何實現的?

我們可以像使用變量一樣使用函數,作為另一個函數的參數,在另一個函數中作為返回結果,在另一個函數中調用它。當我們作為參數傳遞一個回調函數給另一個函數時,我們只傳遞了這個函數的定義,并沒有在參數中執行它。

當包含(調用)函數擁有了在參數中定義的回調函數后,它可以在任何時候調用(也就是回調)它。

這說明回調函數并不是立即執行,而是在包含函數的函數體內指定的位置“回調”它(形如其名)。所以,即使第一個jQuery的例子看起來是這樣:

 

  1. //The anonymous function is not being executed there in the parameter.  
  2. //The item is a callback function 
  3. $("#btn_1").click(function() { 
  4. alert("Btn 1 Clicked"); 
  5. }); 

匿名函數將延遲在click函數的函數體內被調用,即使沒有名稱,也可以被包含函數通過 arguments對象訪問。

回調函數是閉包的

當作為參數傳遞一個回調函數給另一個函數時,回調函數將在包含函數函數體內的某個位置被執行,就像回調函數在包含函數的函數體內定義一樣。這意味著回調函數是閉包的,想更多地了解閉包,請參考作者另一個貼子Understand JavaScript Closures With Ease。從所周知,閉包函數可以訪問包含函數的作用域,所以,回調函數可以訪問包含函數的變量,甚至是全局變量。

三、實現回調函數的基本原則

簡單地說,自己實現回調函數的時候需要遵循幾條原則。

1、使用命名函數或匿名函數作為回調

在前面的jQuery和forEach的例子中,我們在包含函數的參數中定義匿名函數,這是使用回調函數的通用形式之一,另一個經常被使用的形式是定義一個帶名稱的函數,并將函數名作為參數傳遞給另一個函數,例如:

 

  1. ?// global variable 
  2. var allUserData = []; 
  3.  
  4. // generic logStuff function that prints to console 
  5. function logStuff (userData) { 
  6. if ( typeof userData === "string"
  7. console.log(userData); 
  8. else if ( typeof userData === "object"
  9. for (var item in userData) { 
  10. console.log(item + ": " + userData[item]); 
  11.  
  12.  
  13.  
  14. // A function that takes two parameters, the last one a callback function 
  15. function getInput (options, callback) { 
  16. allUserData.push (options); 
  17. callback (options); 
  18.  
  19.  
  20. // When we call the getInput function, we pass logStuff as a parameter. 
  21. // So logStuff will be the function that will called back (or executed) inside the getInput function 
  22. getInput ({name:"Rich", speciality:"JavaScript"}, logStuff); 
  23. // name: Rich 
  24. // speciality: JavaScript 

2、傳遞參數給回調函數

因為回調函數在執行的時候就和一般函數一樣,我們可以傳遞參數給它??梢詫瘮档娜魏螌傩?或全局的屬性)作為參數傳遞回調函數。在上一個例子中,我們將包含函數的options作為參數傳遞給回調函數。下面的例子讓我們傳遞一個全局變量或本地變量給回調函數:

 

 
  1. //Global variable 
  2. var generalLastName = "Clinton"
  3.  
  4. function getInput (options, callback) { 
  5. allUserData.push (options); 
  6. // Pass the global variable generalLastName to the callback function 
  7. callback (generalLastName, options); 

3、在執行之前確?;卣{是一個函數

在調用之前,確保通過參數傳遞進來的回調是一個需要的函數通常是明智的。此外,讓回調函數是可選的也是一個好的實踐。

讓我們重構一下上面例子中的getInput函數,確?;卣{函數做了適當的檢查。

 

 
  1. function getInput(options, callback) { 
  2. allUserData.push(options); 
  3.  
  4. // Make sure the callback is a function 
  5. if (typeof callback === "function") { 
  6. // Call it, since we have confirmed it is callable 
  7. callback(options); 

如果getInput函數沒有做適當的檢查(檢查callback是否是函數,或是否通過參數傳遞進來了),我們的代碼將會導致運行時錯誤。

4、使用含有this對象的回調函數的問題

當回調函數是一個含有this對象的方法時,我們必須修改執行回調函數的方法以保護this對象的內容。否則this對象將會指向全局的window對象(如果回調函數傳遞給了全局函數),或指向包含函數。讓我們看看下面的代碼:

 

 
  1. // Define an object with some properties and a method 
  2. // We will later pass the method as a callback function to another function 
  3. var clientData = { 
  4. id: 094545, 
  5. fullName: "Not Set"
  6. // setUserName is a method on the clientData object 
  7. setUserName: function (firstName, lastName) { 
  8. // this refers to the fullName property in this object 
  9. this.fullName = firstName + " " + lastName; 
  10.  
  11. function getUserInput(firstName, lastName, callback) { 
  12. // Do other stuff to validate firstName/lastName here 
  13.  
  14. // Now save the names 
  15. callback (firstName, lastName); 

在下面的示例代碼中,當clientData.setUserName被執行時,this.fullName不會設置clientData 對象中的屬性fullName,而是設置window 對象中的fullName,因為getUserInput是一個全局函數。出現這種現象是因為在全局函數中this對象指向了window對象。

 

 
  1. getUserInput ("Barack""Obama", clientData.setUserName); 
  2.  
  3. console.log (clientData.fullName);// Not Set 
  4.  
  5. // The fullName property was initialized on the window object 
  6. console.log (window.fullName); // Barack Obama 

5、使用Call或Apply函數保護this對象

我們可以通過使用 Call 或 Apply函數來解決前面示例中的問題。到目前為止,我們知道JavaScript中的每一個函數都有兩個方法:Call和Apply。這些方法可以被用來在函數內部設置this對象的內容,并內容傳遞給函數參數指向的對象。

Call takes the value to be used as the this object inside the function as the first parameter, and the remaining arguments to be passed to the function are passed individually (separated by commas of course). The Apply function's first parameter is also the value to be used as the thisobject inside the function, while the last parameter is an array of values (or the arguments object) to pass to the function. (該段翻譯起來太拗口了,放原文自己體會)

這聽起來很復雜,但讓我們看看Apply和Call的使用是多么容易。為解決前面例子中出現的問題,我們使用Apply函數如下:

 

 
  1. //Note that we have added an extra parameter for the callback object, called "callbackObj" 
  2. function getUserInput(firstName, lastName, callback, callbackObj) { 
  3. // Do other stuff to validate name here 
  4.  
  5. // The use of the Apply function below will set the this object to be callbackObj 
  6. callback.apply (callbackObj, [firstName, lastName]); 

通過Apply函數正確地設置this對象,現在我們可以正確地執行回調函數并它正確地設置clientData對象中的fullName屬性。

 

 
  1. // We pass the clientData.setUserName method and the clientData object as parameters. The clientData object will be used by the Apply function to set the this object 
  2. ?getUserInput ("Barack""Obama", clientData.setUserName, clientData); 
  3.  
  4. // the fullName property on the clientData was correctly set 
  5. console.log (clientData.fullName); // Barack Obama 

我們也可以使用Call 函數,但在本例中我們使用的Apply 函數。

6、多重回調函數也是允許的

我們可以傳遞多個回調函數給另一個函數,就像傳遞多個變量一樣。這是使用jQuery的AJAX函數的典型例子:

 

 
  1. function successCallback() { 
  2. // Do stuff before send 
  3.  
  4. function successCallback() { 
  5. // Do stuff if success message received 
  6.  
  7. function completeCallback() { 
  8. // Do stuff upon completion 
  9.  
  10. function errorCallback() { 
  11. // Do stuff if error received 
  12.  
  13. $.ajax({ 
  14. url:"http://fiddle.jshell.net/favicon.png"
  15. success:successCallback, 
  16. complete:completeCallback, 
  17. error:errorCallback 
  18.  
  19. }); 

四、“回調地獄”的問題和解決方案

異步代碼執行是一種簡單的以任意順序執行的方式,有時是很常見的有很多層級的回調函數,你看起來像下面這樣的代碼。下面這種凌亂的代碼稱作“回調地獄”,因為它是一種包含非常多的回調的麻煩的代碼。我是在node-mongodb-native里看到這個例子的,MongoDB驅動Node.js.示例代碼就像這樣:

 

 
  1. var p_client = new Db('integration_tests_20'new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory}); 
  2. p_client.open(function(err, p_client) { 
  3. p_client.dropDatabase(function(err, done) { 
  4. p_client.createCollection('test_custom_key'function(err, collection) { 
  5. collection.insert({'a':1}, function(err, docs) { 
  6. collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) { 
  7. cursor.toArray(function(err, items) { 
  8. test.assertEquals(1, items.length); 
  9.  
  10. // Let's close the db 
  11. p_client.close(); 
  12. }); 
  13. }); 
  14. }); 
  15. }); 
  16. }); 
  17. }); 

你不太可能在自己的代碼里碰到這個的問題,但如果你碰到了(或以后偶然碰到了),那么有以下兩種方式解決這個問題。

命名并定義你的函數,然后傳遞函數名作為回調,而不是在主函數的參數列表里定義一個匿名函數。

模塊化:把你的代碼劃分成一個個模塊,這樣你可以空出一部分代碼塊做特殊的工作。然后你可以將這個模型引入到你的大型應用程序中。

五、實現自己的回調函數

現在你已經完全理解(我相信你已經理解了,如果沒有請快速重新閱讀一遍)了JavaScript關于回調的所用特性并且看到回調的使用是如此簡單但功能卻很強大。你應該看看自己的代碼是否有機會使用回調函數,有以下需求時你可以考慮使用回調:

避免重復代碼 (DRY—Do Not Repeat Yourself)

在你需要更多的通用功能的地方更好地實現抽象(可處理各種類型的函數)。

增強代碼的可維護性

增強代碼的可讀性

有更多定制的功能

實現自己的回調函數很簡單,在下面的例子中,我可以創建一個函數完成所用的工作:獲取用戶數據,使用用戶數據生成一首通用的詩,使用用戶數據來歡迎用戶,但這個函數將會是一個凌亂的函數,到處是if/else的判斷,甚至會有很多的限制并無法執行應用程序可能需要的處理用戶數據的其它函數。

替而代之的是我讓實現增加了回調函數,這樣主函數獲取用戶數據后可以傳遞用戶全名和性別給回調函數的參數并執行回調函數以完成任何任務。

簡而言之,getUserInput函數是通用的,它可以執行多個擁有各種功能的回調函數。

 

 
  1. // First, setup the generic poem creator function; it will be the callback function in the getUserInput function below. 
  2. function genericPoemMaker(name, gender) { 
  3. console.log(name + " is finer than fine wine."); 
  4. console.log("Altruistic and noble for the modern time."); 
  5. console.log("Always admirably adorned with the latest style."); 
  6. console.log("A " + gender + " of unfortunate tragedies who still manages a perpetual smile"); 
  7.  
  8. //The callback, which is the last item in the parameter, will be our genericPoemMaker function we defined above. 
  9. function getUserInput(firstName, lastName, gender, callback) { 
  10. var fullName = firstName + " " + lastName; 
  11.  
  12. // Make sure the callback is a function 
  13. if (typeof callback === "function") { 
  14. // Execute the callback function and pass the parameters to it 
  15. callback(fullName, gender); 

調用getUserInput函數并傳遞genericPoemMaker函數作為回調:

 

  1. getUserInput("Michael""Fassbender""Man", genericPoemMaker); 
  2. // Output 
  3. /* Michael Fassbender is finer than fine wine. 
  4. Altruistic and noble for the modern time. 
  5. Always admirably adorned with the latest style. 
  6. A Man of unfortunate tragedies who still manages a perpetual smile. 
  7. */ 

因為getUserInput 函數只處理用戶數據的輸入,我們可以傳遞任何回調函數給它。例如我們可以像這樣傳遞一個greetUser函數。

 

 
  1. function greetUser(customerName, sex) { 
  2. var salutation = sex && sex === "Man" ? "Mr." : "Ms."
  3. console.log("Hello, " + salutation + " " + customerName); 
  4.  
  5. // Pass the greetUser function as a callback to getUserInput 
  6. getUserInput("Bill""Gates""Man", greetUser); 
  7.  
  8. // And this is the output 
  9. Hello, Mr. Bill Gates 

和上一個例子一樣,我們調用了同一個getUserInput 函數,但這次卻執行了完全不同的任務。

如你所見,回調函數提供了廣泛的功能。盡管前面提到的例子非常簡單,在你開始使用回調函數的時候思考一下你可以節省多少工作,如何更好地抽象你的代碼。加油吧!在早上起來時想一想,在晚上睡覺前想一想,在你休息時想一想……

我們在JavaScript中經常使用回調函數時注意以下幾點,尤其是現在的web應用開發,在第三方庫和框架中

異步執行(例如讀文件,發送HTTP請求)

事件監聽和處理

設置超時和時間間隔的方法

通用化:代碼簡潔

以上就是更加深入的學習了JavaScript的回調函數,希望對大家的學習有所幫助。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧洲午夜精品久久久| 欧美另类69精品久久久久9999| 欧美日本国产在线| 国产一区视频在线播放| 伊人久久免费视频| 亚洲美女动态图120秒| 亚洲成人av在线播放| 成人高清视频观看www| 伊人成人开心激情综合网| 97视频网站入口| 在线日韩第一页| 国产成人一区三区| 日韩欧美精品免费在线| 亚洲精品狠狠操| 岛国av一区二区在线在线观看| 成人精品福利视频| 日本亚洲欧洲色α| 亚洲在线免费观看| 欧美亚洲激情视频| x99av成人免费| 国产精品www网站| 欧美激情a∨在线视频播放| 久久精品视频在线观看| 亚洲人成在线观看| 成人午夜黄色影院| 欧美成人黄色小视频| 国产精品视频永久免费播放| 欧美精品免费在线| 2023亚洲男人天堂| 久99九色视频在线观看| 一区二区三区回区在观看免费视频| 亚洲女人天堂成人av在线| 亚洲男人的天堂在线| 国产精品久久久久久久久粉嫩av| 欧美激情18p| 青青久久aⅴ北条麻妃| 日韩大片在线观看视频| 国产免费一区二区三区在线观看| 91国自产精品中文字幕亚洲| 亚洲少妇中文在线| 久久777国产线看观看精品| 欧美插天视频在线播放| 91精品视频在线免费观看| 国产精品成人一区二区| 欧美激情在线有限公司| 欧美精品videosex牲欧美| 久热精品视频在线观看| 久久久国产一区二区| xvideos亚洲| 欧美性生交xxxxxdddd| 中文字幕日韩综合av| 国产精品第100页| 在线亚洲欧美视频| 一区二区成人精品| 国内外成人免费激情在线视频| 亚洲999一在线观看www| 欧美日韩福利电影| 日韩精品免费电影| 欧美激情精品久久久久久黑人| 久久成人av网站| 91av视频在线免费观看| 中文国产成人精品久久一| 亚洲欧美精品在线| 青青草国产精品一区二区| 成人激情视频网| 国内精品美女av在线播放| 亚洲老板91色精品久久| 亚洲女性裸体视频| 亚洲高清久久网| 国产中文日韩欧美| 在线观看视频99| 97精品久久久中文字幕免费| 欧美激情图片区| 欧美一级黑人aaaaaaa做受| 亚洲欧美日本另类| 成人黄色免费看| 国产一级揄自揄精品视频| 福利二区91精品bt7086| 中文字幕av一区中文字幕天堂| 性欧美在线看片a免费观看| 精品性高朝久久久久久久| 国产精品一区二区久久久久| 国产精品香蕉在线观看| 成人高清视频观看www| 欧美成人免费全部| 菠萝蜜影院一区二区免费| 国产日韩欧美中文| 国产日产久久高清欧美一区| 日韩精品在线观看一区| 久久精品国亚洲| 在线精品视频视频中文字幕| 日本久久中文字幕| 日韩av大片免费看| 欧美日韩国产中文精品字幕自在自线| 久久精品视频在线| 久久久久久久国产精品| 热久久免费视频精品| 亚洲天堂男人天堂| 欧美激情在线视频二区| 国产精品网站大全| 国产精欧美一区二区三区| 精品久久久久久久久久久久久久| 国产成人综合精品| 国产精品美女999| 欧美极品少妇xxxxⅹ免费视频| 亚洲国产黄色片| 日韩欧美国产黄色| 亚洲午夜未满十八勿入免费观看全集| 国产v综合v亚洲欧美久久| 日日噜噜噜夜夜爽亚洲精品| 国产视频久久网| 91精品国产九九九久久久亚洲| 久久久国产精品免费| 久久久999成人| 久久久久久久久中文字幕| 国产亚洲一级高清| 亚洲精品国产综合区久久久久久久| 日韩a**站在线观看| 欧美与欧洲交xxxx免费观看| 欧美一区二区三区免费视| 日韩欧美在线视频免费观看| 国产在线拍揄自揄视频不卡99| 亚洲福利在线看| 欧美成人激情视频| 欧美一区二区大胆人体摄影专业网站| 亚洲国产一区自拍| 91色在线视频| 日韩亚洲欧美中文高清在线| 另类图片亚洲另类| 日韩中文字幕第一页| 精品国产1区2区| 欧美成在线观看| 久久伊人91精品综合网站| 亚洲精品成a人在线观看| 国产精品人成电影| 热门国产精品亚洲第一区在线| 亚洲伦理中文字幕| 高清一区二区三区四区五区| 精品国产31久久久久久| 久久亚洲综合国产精品99麻豆精品福利| 欧美乱人伦中文字幕在线| 性欧美xxxx交| 国产精品三级久久久久久电影| 国产一区二区三区网站| 成人午夜一级二级三级| 91久久精品国产91久久| 96精品久久久久中文字幕| 激情懂色av一区av二区av| 亚洲国产福利在线| 日韩欧美成人区| 亚洲国产女人aaa毛片在线| 欧美日韩国产中文精品字幕自在自线| 亚洲国产成人久久综合一区| 国产欧美日韩最新| 少妇久久久久久| 国产精品热视频| 国产在线久久久| 国产精品露脸av在线| 亚洲第一页自拍| 国产精品扒开腿做爽爽爽视频| 久久影视电视剧免费网站清宫辞电视| 最近中文字幕日韩精品| 日韩高清电影免费观看完整版| 亚洲白拍色综合图区|