JavaScript中的回調函數(callback)

前言

callback,大家都知道是回調函數的意思。如果讓你舉些callback的例子,我相信你可以舉出一堆。但callback的概念你知道嗎?你自己在實際應用中能不能合理利用回調實現功能? 我們在平時的學習中容易犯不去深究的病,功能實現了也就不再去追其原由,對一些概念模模糊糊。如果對callback沒有一個清楚的理解,估計你在學習Node.js后會崩潰,因為callback是Node.js三大核心之一。

一 .回調函數

回調函數的概念

A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.

以上是Google的解釋,非常清晰簡明,小編令人窒息的四級英語水平都能看懂。
下面給一個回調的例子

function doSomething(msg, callback){
    alert(msg);
    if(typeof callback == "function") 
    callback();
 } 
doSomething("回調函數", function(){
    alert("匿名函數實現回調!");
 }); 

我們再來看幾個經典的回調函數代碼,我保證你一定用過他們:


回調函數例子.png

從上面的例子,我們可以看出回調與同步、異步并沒有直接的聯系,回調只是一種實現方式,既可以有同步回調,也可以有異步回調,還可以有事件處理回調和延遲函數回調,這些在我們工作中有很多的使用場景。


二.把使用this對象的函數作為回調函數(陷阱)

當回調函數是一個使用this對象的函數時,我們必須改變執行回調函數的調用對象來保證this對象的上下文。在講這個問題之前,我們必須先了解JS于this的指向。我們這里不詳細談這個問題,我直接說下自己學習過程總結的判斷this指向的兩條經驗(既然是自己的經驗,那就不一定對,希望大家指正):
(1)this的指向是在函數執行的時候確定的,在函數定義的時候是確定不了,實際上this的最終指向的是那個調用它的對象
(2)調用執行函數時,“.”前面是什么,this就是什么。前面沒有對象,就是window了。(好粗暴)

回到使用this對象的函數作為回調函數這個問題,我們首先來看看下面這段代碼:
//定義一個擁有一些屬性和一個方法的對象 //我們接著將會把方法作為回調函數傳遞給另一個函數

var clientData = {
    id: 096545,
    fullName: "Not Set",
    //setUsrName是一個在clientData對象中的方法
    setUserName: function (firstName, lastName){
        this.fullName = firstName + " " + lastName;
    }
} 

function getUserInput(firstName, lastName, callback){
    //code .....

    //調用回調函數存儲
    callback(firstName, lastName);
}

getUserInput("Barack","Obama",clientData.setUserName);

console.log(clientData.fullName);  //Not Set

console.log(window.fullName);  //Barack Obama

在上面的代碼中,當clientData.setUsername被執行時,this.fullName并沒有設置clientData對象中的fullName屬性。相反,它將設置window對象中的fullName屬性,這是因為callback中的this指向window的緣故。

使用Call和Apply函數來改變this指向

我們可以使用Call或者Apply函數來解決上面你的問題。到目前為止,我們知道了每個Javascript中的函數都有兩個方法:Call 和 Apply。這些方法被用來設置函數內部的this對象以及給此函數傳遞變量。
這里我們演示Apply函數實現,Call函數類似。(call接收的第一個參數為被用來在函數內部當做this的對象,傳遞給函數的參數被挨個傳遞。Apply函數的第一個參數也是在函數內部作為this的對象,然而最后一個參數確是傳遞給函數的值的數組。)

Apply函數:

//注意到我們增加了新的參數作為回調對象,叫做“callbackObj”
function getUserInput(firstName, lastName, callback ,callbackObj){
         //code .....

        callback.apply(callbackObj, [firstName, lastName]);
}

getUserInput("Barack", "Obama", clientData.setUserName, clientData);

console.log(clientData.fullName); //Barack Obama

使用Apply函數正確設置了this對象,我們現在正確的執行了callback并在clientData對象中正確設置了fullName屬性


三.回調函數是實現異步編程的利器

在程序運行中,當某些請求過程漫長,我們有時沒必要選擇等待請求完成繼續處理下一個任務,這時使用回調函數進行異步處理可以大大提高程序執行效率。例如:AJAX請求。若是使用回調函數進行處理,代碼就可以繼續進行其他任務,而無需空等。實際開發中,經常在javascript中使用異步調用!
下面有個使用AJAX加載XML文件的示例,并且使用了call()函數,在請求對象(requested object)上下文中調用回調函數。

function fn(url, callback){
 var httpRequest;    //創建XHR
 httpRequest = window.XMLHttpRequest ? new XMLHttpRequest() :   //針對IE進行功能性檢測
    window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : undefined;
  
 httpRequest.onreadystatechange = function(){
  if(httpRequest.readystate === 4 && httpRequest.status === 200){  //狀態判斷
   callback.call(httpRequest.responseXML); 
  }
 };
 httpRequest.open("GET", url);
 httpRequest.send();
}
 
fn("text.xml", function(){    //調用函數
 console.log(this);                 / /此語句后輸出
});
 
console.log("this will run before the above callback.");  //此語句先輸出

我們請求異步處理,意味著我們開始請求時,就告訴它們完成之時調用我們的函數。在實際情況中,onreadystatechange事件處理程序還得考慮請求失敗的情況,這里我們是假設xml文件存在并且能被瀏覽器成功加載。這個例子中,異步函數分配給了onreadystatechange事件,因此不會立刻執行。
最終,第二個console.log語句先執行,因為回調函數直到請求完成才執行。

在Javascript編程中回調函數經常以幾種方式被使用,尤其是在現代web應用開發以及庫和框架中:

  • 異步調用(例如讀取文件,進行HTTP請求,動態加載js文件,加載iframe資源后,圖片加載完成執行回調等等)
  • 事件監聽器/處理器
  • setTimeout和setInterval方法
  • 一般情況:精簡代碼

4.“回調地獄”問題以及解決方案

這么多回調嵌套,我還沒遇到過。下面內容是直接從網上copy過來,大家看下,還是很好理解的。
在執行異步代碼時,無論以什么順序簡單的執行代碼,經常情況會變成許多層級的回調函數堆積以致代碼變成下面的情形。這些雜亂無章的代碼叫做回調地獄因為回調太多而使看懂代碼變得非常困難。我從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將你的代碼分隔到模塊中,這樣你就可以到處一塊代碼來完成特定的工作。然后你可以在你的巨型應用中導入模塊。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,832評論 18 139
  • 五十三:請解釋 JavaScript 中 this 是如何工作的。1.方法調用模式當一個函數被保存為一個對象的屬性...
    Arno_z閱讀 595評論 0 2
  • @轉自GitHub 介紹js的基本數據類型。Undefined、Null、Boolean、Number、Strin...
    YT_Zou閱讀 1,184評論 0 0
  • 在線閱讀 http://interview.poetries.top[http://interview.poetr...
    前端進階之旅閱讀 114,561評論 24 450
  • ?炎炎夏季 爽膚水大受歡迎 為什么吶? 因為它能給人帶來那種清新的感覺 請點擊此處輸入圖片描 但是有一些爽膚水含有...
    美姬醬閱讀 1,848評論 4 14