這篇文章主要分為以下知識:什麼是Callbacks、Callbacks模型、基本模塊實現、once和auto(memory)、源碼和源碼下載,十分的細致全面,這裡推薦給大家,有需要的小伙伴參考下吧。
代碼的本質突出順序、有序這一概念,尤其在javascript——畢竟javascript是單線程引擎。
javascript擁有函數式編程的特性,而又因為javascript單線程引擎,我們的函數總是需要有序的執行。優秀代碼常常 把函數切割成各自的模塊,然後在某一特定條件下執行,既然這些函數是有序的執行,那麼我們為什麼不編寫一個統一管理的對象,來幫助我們管理這些函數——於是,Callbacks(回調函數)誕生。
什麼是Callbacks
javascript中充斥著函數編程,例如最簡單的window.onload承接的就是一個函數,悲催的是window.onload直接賦值的話只能接收一個函數,如果有好幾個函數想要在onload中執行,那麼我們就需要編寫如下代碼:
代碼如下:
回調函數初衷就是建立在這麼個玩意兒的上面,不再讓我們分散這些函數,而是把這些函數統一整理。可以看見,我們在window.onload中希望針對一個Element做兩件事情:先改變html結構,然後改變這個html的style。兩個函數同樣是針對一個Element操作,而這兩個函數最終的執行都是有序進行的。那麼我們為什麼不編寫一個這樣的對象管理這些函數呢。當然, 這只是回調函數的最基礎的存在意義,我們需要的不僅僅是這樣一個簡單的回調函數對象,我們需要一個更加強大的回調函數。好吧,這只是一個簡單的用例,那麼我可以告訴你這個回調函數除了一個個執行函數之外,它還可以做什麼。
Callbacks本質就是控制函數有序的執行,Javascript是單線程引擎,也就說,javascript同一時間只會有一處代碼在運行——即便是Ajax、setTimeout。 這兩個函數看起來好像都是異步的,其實並非如此,浏覽器在運行javascript代碼的時候,這些代碼都會被有序的壓入一個隊列中,當你運行Ajax的時候,浏覽器會把Ajax 壓入代碼隊列,浏覽器在處理javascript代碼是從這個代碼隊列中一個一個取代碼執行的——Callbacks,迎合了這種單線程引擎。
當然,我們要的,不僅僅是這樣一個簡單的工具對象——在jQuery源碼中,Callbacks提供了一組函數的基本管理,為Deferred(異步隊列)提供了基礎,同時也服務於Queue(同步隊列)。 Deferred用於抹平/扁平化金字塔編程(大量的回調函數嵌套,例如Ajax中需要根據請求返回碼決定執行的代碼); 而Queue,驅動著jQuery.animate(動畫引擎)。
那麼我們就來編寫一個Callbacks吧。
Callbacks模型
Array(數組):
既然我們Callbacks要承接一系列函數,那麼必然需要有一個容器。我們可以使用一個數組,並把每一個函數壓到該數組中,需要執行的時候,循環數組項執行。
工作模型:
這個Callbacks需要非常的強大,並不僅僅是壓入函數,然後執行這麼簡單,這個Callbacks應該擁有良好的執行模型。
once:當前Callbacks對象中所有的函數只會執行一次,執行一次完之後就會被釋放掉,我們可以為使用Callbacks對象的用戶提供一個穩定有效的方案,確保函數只會執行一次,之後不再執行,穩定了這些函數的線程。
auto:自動執行模型,這是個有意思的模型,有些函數依賴上一層函數,例如函數b的執行依賴於函數a,那麼我們提供一個自動執行的模型:第一次執行這個Callbacks之後,每次添加函數到Callbacks的時候,自動執行過去添加的那些函數,並把最後一次給定的參數數據傳遞給過去的那些函數,這樣就從Callbacks中抹平了這些依賴函數之間需要反復觸發的關系,這是個有意思的模型。
once&auto:我們可以讓它更強大,同時工作once和auto模型,即:當每次添加函數到Callbacks中的時候,過去的函數都會執行,然後,釋放掉這些過去的函數,下次繼續添加函數的時候,過去的那些函數不會再執行,因為once模型,已經把它們釋放掉了。
API:
add(function) - 添加一個(或多個)函數到Callbacks對象中:當然,如果你並不添加函數只是好奇看看Callbacks,我們也將讓你繼續享受你的樂趣——我們並不會拋出異常,因為這對於我們來說並不擅長。
remove(function) - 移除一個Callbacks中的一個函數:既然有了添加,那麼我們也應該提供反悔的方案,我們是多麼的平易近人,容忍著別人過去所做的一切。
has(function) - 判斷Callbacks中是否包含一個函數:哦?你竟然不確定是否包含這個函數,當初可是你丟進來的啊!你怎麼如此馬虎?不過既然你問我的話,我仍然會告訴你Callbacks是否包含這個函數,我知道你很繁忙,並不能記住和確定所有的事情。
empty() - 清空Callbacks:這些函數對於你失去了意義了麼?什麼?已經執行過你就不想要了?所以你希望可以清空它?好吧,為了內存君我還是忍下你這個需求。
disable() - 廢掉一個Callbacks:為了和別人的代碼穩定的存在,我選擇了自我犧牲——沒錯,這個方法可以廢掉Callbacks,徹底的廢掉,就如同它曾經尚未存在過一般。
disabled() - 判斷這個Callbacks是否已經被廢掉:如果你仍然不相信Callbacks是否真的自我犧牲,那麼這個方法可以讓你安心。
lock(boolean) - 鎖定這個Callbacks對象:你害怕它並不穩定,但是你又不想捨棄它,lock是個不錯的方法,它接收一個Boolean的參數,表示是否需要鎖定這個對象,當然,無參的它用於讓你確定Callbacks是否被鎖定。
fire(data) - 執行這個Callbacks中的函數:我們做的這一切,不都是為了這一刻執行的宿命麼?參數將會成為這些需要執行的函數的參數。
fireWith(context,data) - 執行Callbacks中的函數,並且指定上下文。在fire()裡,所有的函數的Context(上下文)都是Callbacks對象,而fireWidth(),可以讓你重新定義這些要執行的函數的上下文,多麼自由的編程啊,Callbacks為你考慮了一切。
fired() - 判斷這個Callbacks過去是否已經執行過:我們相信,多數時候你並不知道過去做過什麼,但是我們記錄了你做的一切,如果你過去曾經執行過這個Callbacks對象,那麼你休想否認,因為我們知道過去你是否執行了這個Callbacks。
基本模塊實現
簡單的實現:
我們先來簡單的實現一個Callbacks:
代碼如下:
然後我們測試一下這個Callbacks:
代碼如下:
打開浏覽器的控制台我們可以看見運行結果正常。
once和auto(memory)實現
once:
once讓這個callbacks中的函數運行一次