這里我想跟你聊聊我理解的回調(diào)函數(shù)(callback)煮仇。
回調(diào)函數(shù),我覺得可以理解成作為參數(shù)傳遞的函數(shù)對(duì)象舔示。因?yàn)樵?JavaScript 中函數(shù)有比較高的等級(jí)(是一種基本類型尊浓,嗯粪狼,應(yīng)該可以這樣講吧)退腥,可以獨(dú)立地進(jìn)行使用。
而在 Java 中再榄,可能只有作為類的方法的“函數(shù)”存在吧狡刘,不存在可以單獨(dú)使用的函數(shù)。當(dāng)然在 Java 中困鸥,我“揣測(cè)”函數(shù)嗅蔬,或者叫方法(method)更合適,可能只有被調(diào)用的使用方式疾就,例如: foo.method()
澜术。
在 JavaScript 中,函數(shù)還是挺獨(dú)立的猬腰。例如鸟废,盡管有的時(shí)候一個(gè)函數(shù)在聲明的時(shí)候就是作為某個(gè)對(duì)象的方法的形式,但仍然可以撇開這個(gè)對(duì)象來使用姑荷。這里用到了函數(shù)的 .call()
或 .apply()
方法(這里就叫方法吧盒延,不再繞來繞去了)。例如:
var Person = function (name) {
this.name = name;
this.getName = function () {
return this.name;
};
};
我先用自己的話跟你大致說下上面的代碼里面做了什么事情鼠冕,有些東西是我之前的文章還沒有講到的添寺,不過相信你可能也都懂的:
首先,我聲明了一個(gè)變量和一個(gè)函數(shù)懈费,函數(shù)本身沒有名稱计露,將其作為值賦給了變量
Person
,這是最主要的結(jié)構(gòu);然后票罐,再來看這個(gè)匿名函數(shù)的內(nèi)部叉趣,我給
this
對(duì)象(且這么叫吧)設(shè)置了兩個(gè)屬性,屬性name
的值為匿名函數(shù)的參數(shù)name
胶坠,屬性getName
的值為一個(gè)匿名函數(shù);最后繁堡,我們看下
getName
對(duì)應(yīng)的匿名函數(shù)干了什么:它返回了this
的屬性name
的值沈善。
對(duì)于 Person
的使用方式,之前提到過(不過定義的形式可能稍有不同椭蹄,這不是重點(diǎn))闻牡,可以這樣用:
var me = new Person("luobo");
me.getName(); // "luobo"
這里說了挺多題外的東西,現(xiàn)在我們來看上面 me
這個(gè)對(duì)象的方法 getName()
绳矩。盡管形式上是對(duì)象的方法罩润,而且在一開始定義的時(shí)候就確定了其方法的目的(這種 this.foo = ...
的方式我會(huì)在講繼承的時(shí)候再跟你多聊一些),但是由于其函數(shù)的本質(zhì)翼馆,仍然可以單獨(dú)來使用:
me.getName.call({ name: "Mickey" }); // "Mickey"
注意割以,這里 me.getName
只是為了引用到實(shí)際的函數(shù)(對(duì)象),以 .call()
來調(diào)用時(shí)傳入了新的“上下文對(duì)象” { name: "Mickey" }
应媚,而這個(gè)上下文對(duì)象在函數(shù)執(zhí)行時(shí)會(huì)替換函數(shù)內(nèi)部使用的 this
严沥,有點(diǎn)了解了嗎?
所以中姜,實(shí)際上對(duì)于上文中聲明的 Person 所對(duì)應(yīng)的匿名函數(shù)消玄,也可以這樣來使用:
var me = {};
Person.call(me, "luobo");
me.getName(); // "luobo"
再引申一點(diǎn)來講,使用 new Person()
這樣的形式來獲得一個(gè)對(duì)象丢胚,那么在 Person
內(nèi)部的 this
就是對(duì)應(yīng)這個(gè)即將返回的對(duì)象 翩瓜。(嚴(yán)格來說不完全是,在 Person
對(duì)應(yīng)的匿名函數(shù)有明確定義的其他返回值時(shí)就不再返回這個(gè) this
對(duì)象啦)
雖然是要講回調(diào)函數(shù)携龟,可是我花了好多時(shí)間來講每個(gè)函數(shù)都可以被獨(dú)立調(diào)用這件事兔跌,還說到函數(shù)中的 this
在函數(shù)執(zhí)行時(shí)可以明確指定(通過 .call()
傳入的第一個(gè)參數(shù)即作為函數(shù)內(nèi)部 this
所指向的對(duì)象),這些都是會(huì)用到的峡蟋。
下面我舉個(gè)使用回調(diào)函數(shù)的栗子:
$("#myDiv").on("click", function () {
this.innerHTML = "you clicked me!";
});
這個(gè)栗子中浮定,我們使用 jQuery 給一個(gè) id 為 myDiv 的元素綁定了 click 事件的處理程序,這里的事件處理程序就是一個(gè)回調(diào)函數(shù)层亿。當(dāng)然回調(diào)函數(shù)并非是一種特殊類型的對(duì)象桦卒,其實(shí)就是普通的函數(shù),但是被作為其他函數(shù)的參數(shù)傳遞匿又,在某個(gè)時(shí)刻才會(huì)被選擇性地使用方灾。
單純看回調(diào)函數(shù)不是特別有趣,不過你應(yīng)該注意到上面這個(gè)回調(diào)函數(shù)的定義中使用 this
,那么這個(gè) this
又是指向什么對(duì)象呢裕偿?
實(shí)際上洞慎,這是由 jQuery 提供的機(jī)制,在事件處理程序被調(diào)用時(shí)嘿棘,this
會(huì)指向事件的目標(biāo)對(duì)象劲腿,這里也就是被點(diǎn)擊的 myDiv 元素,而 HTML 元素有 innerHTML 屬性鸟妙,就是上面的使用方式了焦人。這是怎么實(shí)現(xiàn)的呢,合理揣測(cè)下的話重父,肯定是這個(gè)回調(diào)函數(shù)在調(diào)用是被顯式指定了“上下文對(duì)象” this
花椭,有可能就是通過 .call()
或 .apply()
的方式。
另外房午,由于回調(diào)函數(shù)會(huì)在什么時(shí)候被執(zhí)行矿辽,可能是不確定,甚至也可能永遠(yuǎn)不會(huì)被執(zhí)行郭厌。而且在上面的這種情況下袋倔,這個(gè)回調(diào)函數(shù)甚至要在未來的某個(gè)時(shí)候才會(huì)執(zhí)行,這就有了一個(gè)“異步”的感覺折柠,也就是說代碼不是從上到下依次執(zhí)行奕污,前面的代碼執(zhí)行完畢后面的才會(huì)執(zhí)行(這個(gè)可以叫做“同步”啦)。
在使用 Ajax 時(shí)液走,我們可以指定請(qǐng)求是同步還是異步的方式碳默,同步的方式下比較好理解,一定是這個(gè)請(qǐng)求接收到服務(wù)器響應(yīng)或者超時(shí)失敗后缘眶,后面的代碼才會(huì)執(zhí)行嘱根。這種情況下我們編寫需要在一個(gè) Ajax 請(qǐng)求后才能做的時(shí)候,例如 alert("Ajax 請(qǐng)求已完成巷懈!")
该抒,是比較容易的。不過壞處是代碼在執(zhí)行到這里的時(shí)候會(huì)等待 Ajax 請(qǐng)求被響應(yīng)顶燕,一切都停了下來凑保。如果不想讓世界暫停下來,就可以使用 Ajax 異步的模式(這也是通常的使用方式)涌攻,然后把想要做的事情以回調(diào)函數(shù)的形式包裝一下欧引,等待請(qǐng)求在未來有個(gè)結(jié)果(響應(yīng)、超時(shí)恳谎、意外終止等)后執(zhí)行芝此。舉個(gè)栗子:
$.ajax("/foo.jsp").done(function () {
alert("成功憋肖!");
}).fail(function () {
alert("失敗婚苹!");
});
這里分別為請(qǐng)求成功和失敗兩種情況指定了回調(diào)函數(shù)岸更。
關(guān)于回調(diào)函數(shù),咱們就聊這么多吧膊升。
擴(kuò)展:
- 關(guān)于“異步”執(zhí)行怎炊,有沒有考慮過專門去使用它的情況?
例如廓译,代碼執(zhí)行時(shí)間預(yù)期會(huì)很長(zhǎng)评肆,為避免瀏覽器不響應(yīng)用戶交互,人為地將代碼分為多個(gè)部分分別執(zhí)行责循,從而使得每個(gè)部分執(zhí)行時(shí)間不會(huì)很長(zhǎng)糟港。 - 主動(dòng)地讓代碼“異步”執(zhí)行的方法攀操?
常見的有setTimeout
院仿,另外還有借助瀏覽器的事件機(jī)制實(shí)現(xiàn)的,如為新創(chuàng)建的 img 元素指定 onload 事件的方式等等速和。 - 異步執(zhí)行的代碼多層嵌套的處理
因?yàn)橐曰卣{(diào)函數(shù)的方式來編寫異步執(zhí)行代碼歹垫,可能會(huì)很多級(jí)匿名函數(shù)互相嵌套的情況,代碼可讀性會(huì)比較差颠放。這種情況下一個(gè)選擇是可以使用一些第三方的庫排惨,如 when.js。其實(shí) jQuery 也提供了異步機(jī)制碰凶,可以看下 deffered暮芭,我們常用的 jQuery 的 ajax 函數(shù)也基于這個(gè)東東改造過。
接下來還想跟你聊聊:
- JavaScript - 作用域和“閉包”
- JavaScript - 繼承和“類”