摘要:在閱讀本文之后你能了解怎樣使用回調函數。這是中回調函數的典型用法,它在中廣泛被使用。這意味著回調函數本質上是一個閉包。使用命名或匿名函數作為回調在前面的例子以及的例子中,我們使用了再參數位置定義的匿名函數作為回調函數。
轉自:
張小俊128:http://www.html-js.com/articl...
_luxiao:http://luxiao1223.blog.51cto....
js里的解釋:
A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.
字面上理解下來就是,回調就是一個函數的調用過程。那么就從理解這個調用過程開始吧。函數a有一個參數,這個參數是個函數b,當函數a執行完以后執行函數b。那么這個過程就叫回調。
其實中文也很好理解:回調,回調,就是回頭調用的意思。函數a的事先干完,回頭再調用函數b。
舉個現實的例子:約會結束后你送你女朋友回家,離別時,你肯定會說:“到家了給我發條信息,我很擔心你。”對不,然后你女朋友回家以后還真給你發了條信息。小伙子,你有戲了。
其實這就是一個回調的過程。你留了個函數b(要求女朋友給你發條信息)給你女朋友,然后你女朋友回家,回家的動作是函數a。她必須先回到家以后,函數a的內容執行完了,再執行函數b,然后你就收到一條信息了。
這里必須清楚一點:函數b是你以參數形式傳給函數a的,那么函數b就叫回調函數。
也許有人有疑問了:一定要以參數形式傳過去嗎,我不可以直接在函數a里面調用函數b嗎?確實可以。求解中。
<解惑:如果你直接在函數a里調用的話,那么這個回調函數就被限制死了。但是使用函數做參數就有下面的好處:當你a(b)的時候函數b就成了回調函數,而你還可以a(c)這個時候,函數c就成了回調函數。如果你寫成了function a(){...;b();}就失去了變量的靈活性。>
下面用代碼來證實我的理解。
回調函數(callback) 學習js回調函數
應該能看到調用了兩個回調函數
前言:2013年11月20日
在Javascript中,函數是第一類對象,這意味著函數可以像對象一樣按照第一類管理被使用。既然函數實際上是對象:它們能被“存儲”在變量中,能作為函數參數被傳遞,能在函數中被創建,能從函數中返回。
因為函數是第一類對象,我們可以在Javascript使用回調函數。在下面的文章中,我們將學到關于回調函數的方方面面。回調函數可能是在Javascript中使用最多的函數式編程技巧,雖然在字面上看起來它們一直一小段Javascript或者jQuery代碼,但是對于許多開發者來說它任然是一個謎。在閱讀本文之后你能了解怎樣使用回調函數。
回調函數是從一個叫函數式編程的編程范式中衍生出來的概念。簡單來說,函數式編程就是使用函數作為變量。函數式編程過去 - 甚至是現在,依舊沒有被廣泛使用 - 它過去常被看做是那些受過特許訓練的,大師級別的程序員的秘傳技巧。
幸運的是,函數是編程的技巧現在已經被充分闡明因此像我和你這樣的普通人也能去輕松使用它。函數式編程中的一個主要技巧就是回調函數。在后面內容中你會發現實現回調函數其實就和普通函數傳參一樣簡單。這個技巧是如此的簡單以致于我常常感到很奇怪為什么它經常被包含在講述Javascript高級技巧的章節中。
什么是回調或者高階函數
一個回調函數,也被稱為高階函數,是一個被作為參數傳遞給另一個函數(在這里我們把另一個函數叫做“otherFunction”)的函數,回調函數在otherFunction中被調用。一個回調函數本質上是一種編程模式(為一個常見問題創建的解決方案),因此,使用回調函數也叫做回調模式。
下面是一個在jQuery中使用回調函數簡單普遍的例子:
/注意到click方法中是一個函數而不是一個變量
//它就是回調函數 $("#btn_1").click(function() { alert("Btn 1 Clicked"); });
正如你在前面的例子中看到的,我們將一個函數作為參數傳遞給了click方法。click方法會調用(或者執行)我們傳遞給它的函數。這是Javascript中回調函數的典型用法,它在jQuery中廣泛被使用。
下面是另一個Javascript中典型的回調函數的例子:
var friends = ["Mike", "Stacy", "Andy", "Rick"]; friends.forEach(function (eachName, index){ console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick });
再一次,注意到我們講一個匿名函數(沒有名字的函數)作為參數傳遞給了forEach方法。
到目前為止,我們將匿名函數作為參數傳遞給了另一個函數或方法。在我們看更多的實際例子和編寫我們自己的回調函數之前,先來理解回調函數是怎樣運作的。
回調函數是怎樣運作的?
因為函數在Javascript中是第一類對象,我們像對待對象一樣對待函數,因此我們能像傳遞變量一樣傳遞函數,在函數中返回函數,在其他函數中使用函數。當我們將一個回調函數作為參數傳遞給另一個函數是,我們僅僅傳遞了函數定義。我們并沒有在參數中執行函數。我們并不傳遞像我們平時執行函數一樣帶有一對執行小括號()的函數。
需要注意的很重要的一點是回調函數并不會馬上被執行。它會在包含它的函數內的某個特定時間點被“回調”(就像它的名字一樣)。因此,即使第一個jQuery的例子如下所示:
//匿名函數不會再參數中被執行 //這是一個回調函數 $("#btn_1").click(function(){ alert("Btn 1 Clicked"); });
這個匿名函數稍后會在函數體內被調用。即使有名字,它依然在包含它的函數內通過arguments對象獲取。
回調函數是閉包
都能夠我們將一個毀掉函數作為變量傳遞給另一個函數時,這個毀掉函數在包含它的函數內的某一點執行,就好像這個回調函數是在包含它的函數中定義的一樣。這意味著回調函數本質上是一個閉包。
正如我們所知,閉包能夠進入包含它的函數的作用域,因此回調函數能獲取包含它的函數中的變量,以及全局作用域中的變量。
實現回調函數的基本原理
回調函數并不復雜,但是在我們開始創建并使用毀掉函數之前,我們應該熟悉幾個實現回調函數的基本原理。
使用命名或匿名函數作為回調
在前面的jQuery例子以及forEach的例子中,我們使用了再參數位置定義的匿名函數作為回調函數。這是在回調函數使用中的一種普遍的魔術。另一種常見的模式是定義一個命名函數并將函數名作為變量傳遞給函數。比如下面的例子:
//全局變量 var allUserData = []; //普通的logStuff函數,將內容打印到控制臺 function logStuff (userData){ if ( typeof userData === "string") { console.log(userData); } else if ( typeof userData === "object"){ for(var item in userData){ console.log(item + ": " + userData[item]); } } } //一個接收兩個參數的函數,后面一個是回調函數 function getInput (options, callback){ allUserData.push(options); callback(options); } //當我們調用getInput函數時,我們將logStuff作為一個參數傳遞給它 //因此logStuff將會在getInput函數內被回調(或者執行) getInput({name:"Rich",speciality:"Javascript"}, logStuff); //name:Rich //speciality:Javascript
傳遞參數給回調函數
既然回調函數在執行時僅僅是一個普通函數,我們就能給它傳遞參數。我們能夠傳遞任何包含它的函數的屬性(或者全局書訊給)作為回調函數的參數。在前面的例子中,我們將options作為一個參數傳遞給了毀掉函數。現在我們傳遞一個全局變量和一個本地變量:
//全局變量 var generalLastName = "Cliton"; function getInput (options, callback){ allUserData.push (options); //將全局變量generalLastName傳遞給回調函數 callback(generalLastName,options); }
在執行之前確保回調函數是一個函數
在調用之前檢查作為參數被傳遞的回調函數確實是一個函數,這樣的做法是明智的。同時,這也是一個實現條件回調函數的最佳時間。
我們來重構上面例子中的getInput函數來確保檢查是恰當的。
function getInput(options, callback){ allUserData.push(options); //確保callback是一個函數 if(typeof callback === "function"){ //調用它,既然我們已經確定了它是可調用的 callback(options); } }
如果沒有適當的檢查,如果getInput的參數中沒有一個回調函數或者傳遞的回調函數事實上并不是一個函數,我們的代碼將會導致運行錯誤。
使用this對象的方法作為回調函數時的問題
當回調函數是一個this對象的方法時,我們必須改變執行回調函數的方法來保證this對象的上下文。否則如果回調函數被傳遞給一個全局函數,this對象要么指向全局window對象(在瀏覽器中)。要么指向包含方法的對象。
我們在下面的代碼中說明:
//定義一個擁有一些屬性和一個方法的對象 //我們接著將會把方法作為回調函數傳遞給另一個函數 var clientData = { id: 094545, fullName "Not Set", //setUsrName是一個在clientData對象中的方法 setUserName: fucntion (firstName, lastName){ //這指向了對象中的fullName屬性 this.fullName = firstName + " " + lastName; } } function getUserInput(firstName, lastName, callback){ //在這做些什么來確認firstName/lastName //現在存儲names callback(firstName, lastName); }
在下面你的代碼例子中,當clientData.setUsername被執行時,this.fullName并沒有設置clientData對象中的fullName屬性。相反,它將設置window對象中的fullName屬性,因為getUserInput是一個全局函數。這是因為全局函數中的this對象指向window對象。
getUserInput("Barack","Obama",clientData.setUserName); console.log(clientData,fullName); //Not Set //fullName屬性將在window對象中被初始化 console.log(window.fullName); //Barack Obama
使用Call和Apply函數來保存this
我們可以使用Call或者Apply函數來修復上面你的問題。到目前為止,我們知道了每個Javascript中的函數都有兩個方法:Call 和 Apply。這些方法被用來設置函數內部的this對象以及給此函數傳遞變量。
call接收的第一個參數為被用來在函數內部當做this的對象,傳遞給函數的參數被挨個傳遞(當然使用逗號分開)。Apply函數的第一個參數也是在函數內部作為this的對象,然而最后一個參數確是傳遞給函數的值的數組。
聽起來很復雜,那么我們來看看使用Apply和Call有多么的簡單。為了修復前面例子的問題,我將在下面你的例子中使用Apply函數:
//注意到我們增加了新的參數作為回調對象,叫做“callbackObj” function getUserInput(firstName, lastName, callback. callbackObj){ //在這里做些什么來確認名字 callback.apply(callbackObj, [firstName, lastName]); }
使用Apply函數正確設置了this對象,我們現在正確的執行了callback并在clientData對象中正確設置了fullName屬性:
//我們將clientData.setUserName方法和clientData對象作為參數,clientData對象會被Apply方法使用來設置this對象 getUserInput("Barack", "Obama", clientData.setUserName, clientData); //clientData中的fullName屬性被正確的設置 console.log(clientUser.fullName); //Barack Obama
我們也可以使用Call函數,但是在這個例子中我們使用Apply函數。
允許多重回調函數
我們可以將不止一個的回調函數作為參數傳遞給一個函數,就像我們能夠傳遞不止一個變量一樣。這里有一個關于jQuery中AJAX的例子:
function successCallback(){ //在發送之前做點什么 } function successCallback(){ //在信息被成功接收之后做點什么 } function completeCallback(){ //在完成之后做點什么 } function errorCallback(){ //當錯誤發生時做點什么 } $.ajax({ url:"http://fiddle.jshell.net/favicon.png", success:successCallback, complete:completeCallback, error:errorCallback });
“回調地獄”問題以及解決方案
在執行異步代碼時,無論以什么順序簡單的執行代碼,經常情況會變成許多層級的回調函數堆積以致代碼變成下面的情形。這些雜亂無章的代碼叫做回調地獄因為回調太多而使看懂代碼變得非常困難。我從node-mongodb-native,一個適用于Node.js的MongoDB驅動中拿來了一個例子。這段位于下方的代碼將會充分說明回調地獄:
var p_client = new Db("integration_tests_20", new Server("127.0.0.1", 27017, {}), {"pk":CustomPKFactory}); p_client.open(function(err, p_client) { p_client.dropDatabase(function(err, done) { p_client.createCollection("test_custom_key", function(err, collection) { collection.insert({"a":1}, function(err, docs) { collection.find({"_id":new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) { cursor.toArray(function(err, items) { test.assertEquals(1, items.length); // Let"s close the db p_client.close(); }); }); }); }); }); });
你應該不想在你的代碼中遇到這樣的問題,當你當你遇到了-你將會是不是的遇到這種情況-這里有關于這個問題的兩種解決方案。
給你的函數命名并傳遞它們的名字作為回調函數,而不是主函數的參數中定義匿名函數。
模塊化L將你的代碼分隔到模塊中,這樣你就可以到處一塊代碼來完成特定的工作。然后你可以在你的巨型應用中導入模塊。
創建你自己的回調函數
既然你已經完全理解了關于Javascript中回調函數的一切(我認為你已經理解了,如果沒有那么快速的重讀以便),你看到了使用回調函數是如此的簡單而強大,你應該查看你的代碼看看有沒有能使用回調函數的地方。回調函數將在以下幾個方面幫助你:
避免重復代碼(DRY-不要重復你自己)
在你擁有更多多功能函數的地方實現更好的抽象(依然能保持所有功能)
讓代碼具有更好的可維護性
使代碼更容易閱讀
編寫更多特定功能的函數
創建你的回調函數非常簡單。在下面的例子中,我將創建一個函數完成以下工作:讀取用戶信息,用數據創建一首通用的詩,并且歡迎用戶。這本來是個非常復雜的函數因為它包含很多if/else語句并且,它將在調用那些用戶數據需要的功能方面有諸多限制和不兼容性。
相反,我用回調函數實現了添加功能,這樣一來獲取用戶信息的主函數便可以通過簡單的將用戶全名和性別作為參數傳遞給回調函數并執行來完成任何任務。
簡單來講,getUserInput函數是多功能的:它能執行具有無種功能的回調函數。
//首先,創建通用詩的生成函數;它將作為下面的getUserInput函數的回調函數 function genericPoemMaker(name, gender) { console.log(name + " is finer than fine wine."); console.log("Altruistic and noble for the modern time."); console.log("Always admirably adorned with the latest style."); console.log("A " + gender + " of unfortunate tragedies who still manages a perpetual smile"); } //callback,參數的最后一項,將會是我們在上面定義的genericPoemMaker函數 function getUserInput(firstName, lastName, gender, callback) { var fullName = firstName + " " + lastName; // Make sure the callback is a function if (typeof callback === "function") { // Execute the callback function and pass the parameters to it callback(fullName, gender); } } 調用getUserInput函數并將genericPoemMaker函數作為回調函數: getUserInput("Michael", "Fassbender", "Man", genericPoemMaker); // 輸出 /* Michael Fassbender is finer than fine wine. Altruistic and noble for the modern time. Always admirably adorned with the latest style. A Man of unfortunate tragedies who still manages a perpetual smile. */
因為getUserInput函數僅僅只負責提取數據,我們可以把任意回調函數傳遞給它。例如,我們可以傳遞一個greetUser函數:
function greetUser(customerName, sex) { var salutation = sex && sex === "Man" ? "Mr." : "Ms."; console.log("Hello, " + salutation + " " + customerName); } // 將greetUser作為一個回調函數 getUserInput("Bill", "Gates", "Man", greetUser); // 這里是輸出 Hello, Mr. Bill Gates
我們調用了完全相同的getUserInput函數,但是這次完成了一個完全不同的任務。
正如你所見,回調函數很神奇。即使前面的例子相對簡單,想象一下能節省多少工作量,你的代碼將會變得更加的抽象,這一切只需要你開始使用毀掉函數。大膽的去使用吧。
在Javascript編程中回調函數經常以幾種方式被使用,尤其是在現代web應用開發以及庫和框架中:
異步調用(例如讀取文件,進行HTTP請求,等等)
時間監聽器/處理器
setTimeout和setInterval方法
一般情況:精簡代碼
結束語
Javascript回調函數非常美妙且功能強大,它們為你的web應用和代碼提供了諸多好處。你應該在有需求時使用它;或者為了代碼的抽象性,可維護性以及可讀性而使用回調函數來重構你的代碼。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/82616.html
摘要:調用棧被清空,消息隊列中并無任務,線程停止,事件循環結束。不確定的時間點請求返回,將設定好的回調函數放入消息隊列。調用棧執行完畢執行消息隊列任務。請求并發回調函數執行順序無法確定。 異步編程 JavaScript中異步編程問題可以說是基礎中的重點,也是比較難理解的地方。首先要弄懂的是什么叫異步? 我們的代碼在執行的時候是從上到下按順序執行,一段代碼執行了之后才會執行下一段代碼,這種方式...
摘要:的翻譯文檔由的維護很多人說,阮老師已經有一本關于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。若是使用回調函數進行處理,代碼就可以繼續進行其他任務,而無需空等。參考理解回調函數理解與使用中的回調函數這篇相當不錯回調函數 為什么寫回調函數 對于javascript中回調函數 一直處于理解,但是應用不好的階段,總是在別人家的代碼中看到很巧妙的回調,那時候會有wow c...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。寫一個符合規范并可配合使用的寫一個符合規范并可配合使用的理解的工作原理采用回調函數來處理異步編程。 JavaScript怎么使用循環代替(異步)遞歸 問題描述 在開發過程中,遇到一個需求:在系統初始化時通過http獲取一個第三方服務器端的列表,第三方服務器提供了一個接口,可通過...
摘要:接下來,我們一起來看看中的異步編程,具體有哪幾種。實現異步編程的方法一回調函數上面不止一次提到了回調函數。它是異步編程中,最基本的方法。四對象接下來,我們聊聊與相關的異步編程方法,對象。 showImg(https://segmentfault.com/img/bVbneWy?w=1600&h=1200); 前言 最近,小伙伴S 問了我一段代碼: const funB = (value...
閱讀 1590·2023-04-25 15:50
閱讀 1309·2021-09-22 15:49
閱讀 2938·2021-09-22 15:06
閱讀 3593·2019-08-30 15:54
閱讀 2338·2019-08-29 11:33
閱讀 2123·2019-08-23 17:56
閱讀 2153·2019-08-23 17:06
閱讀 1303·2019-08-23 15:55