摘要:事件驅動正是一種回調函數設計模式。由于不支持多線程,所以為了實現并行處理,不得不使用回調函數,這逐漸成為了一種慣例。上面的回調函數只是單純的函數而不具有狀態。如果回調函數具有狀態,就能得到更為廣泛的應用。
回調函數模式 回調函數與控制反轉
回調函數是程序設計的一種方法。這種方法是指,在傳遞了可能會進行調用的函數或對象之后,在需要時再分別對其進行調用。由于調用方與被調用方的依賴關系與通常相反,所以也成為控制反轉(IoC,Inversion of Control)。
由于歷史原因,在JavaScript開發中我們常常會用到回調函數這一方法,這是多種因素導致的。第一個原因是在客戶端JavaScript中基本都是GUI程序設計。GUI程序設計是一種很適合使用所謂事件驅動的程序設計方式。事件驅動正是一種回調函數設計模式??蛻舳薐avaScript程序設計是一種基于DOM的事件驅動式程序設計。
第二個原因是,源于客戶端無法實現多線程程序設計(最近HTML5 Web Works支持多線程了)。而通過將回調函數與異步處理相結合,就能夠實現并行處理。由于不支持多線程,所以為了實現并行處理,不得不使用回調函數,這逐漸成為了一種慣例。最后一個原因與JavaScript中的函數聲明表達式和閉包有關。
var emitter = { // 為了能夠注冊多個回調函數而通過數組管理 callbacks:[], // 回調函數的注冊方法 register:function (fn) { this.callbacks.push(fn); }, // 事件的觸發處理 onOpen:function () { for (var f in this.callbacks) { this.callbacks[f](); } } }; emitter.register(function () {alert("event handler1 is called");}) emitter.register(function () {alert("event handler2 is called");}) emitter.onOpen(); // "event handler1 is called" // "event handler2 is called"
定義的兩個匿名函數就是回調函數,它們的調用由emitter.onOpen()完成。
對emitter來說,這僅僅是對注冊的函數進行了調用,不過根據回調函數的定義,更應該關注使用了emitter部分的情況。從這個角度來看,注冊過的回調函數與之形成的是一種調用與被調用的關系。
上面的回調函數只是單純的函數而不具有狀態。如果回調函數具有狀態,就能得到更為廣泛的應用。下面我們把回調方改為了對象,于是emitter變為了接受方法傳遞的形式。
function MyClass(msg) { this.msg = msg; this.show = function () {alert(this.msg+" is called");} } // 將方法注冊為回調函數 var obj = new MyClass("listener1"); var obj2 = new MyClass("listener2"); emitter.register(obj.show); emitter.register(obj2.show); emitter.onOpen(); // undefined is called // undefined is called
我們發現,調用回調函數無法正確顯示this.msg,錯誤原因在于JavaScript內的this引用。解決方法有兩種,一種是使用bind,一種是不使用方法而是用對象進行注冊。后者在JavaScript中并不常用。
emitter.register(obj.show.bind(obj)); emitter.register(obj2.show.bind(obj2)); emitter.onOpen(); // "listener1 is called" // "listener2 is called"
bind是ES5新增的功能,是Function.prototype對象的方法。bind的作用和apply與call相同,都是用于明確指定出方法調用時的this引用。對于函數來說,調用了bind之后會返回一個新函數,新的函數會執行與原函數相同的內容,不過其this引用是被指定為它的第一個參數的對象。在調用apply與call時將會立即調用目標函數,而在調用bind時則不會如此,而是會返回一個函數(閉包)。
如果使用了apply或call,就能對bind進行獨立的實現。事實上在ES5才推出之前,在prototype.js等知名的庫中就通過apply/call提供了bind自己的實現。
腦補的bind的內部實現?
Function.prototype.bind = null; Function.prototype.bind = function (obj) { var f = this; return function () { f.call(obj); } } var obj = { x:"這是 obj.x !!!", fn:function () { alert(this.x); } }; var obj2 = {x:"obj2.x 對啦?。?!"}; var testfn = obj.fn.bind(obj2); testfn(); // "obj2.x 對啦!??!"
閉包與回調函數
emitter.register( (function () { var msg = "closure1"; return function () {alert(msg+" is called;")}; }()) ); emitter.register( (function () { var msg = "closure2"; return function () {alert(msg+" is called;")}; }()) ) emitter.onOpen(); // "closure1 is called" // "closure2 is called"
借助閉包,前面繁復的說明仿佛不在存在,可以很輕松的實現回調函數,并且還能像對象一樣具有狀態。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83473.html
摘要:函數式編程最后介紹一下函數式編程。函數式編程是一種歷史悠久,而又在最近頗為熱門的話題。函數式編程在面向對象一詞誕生以前就已經存在,不過它在很長一段時間里都被隱藏于過程式編程面向對象也是過程式編程的一種的概念之下。 2.1 JavaScript特點 總結以下幾個特點: 解釋型語言 類似與C和Java的語法結構 動態語言 基于原型的面向對象 字面量的表現能力 函數式編程 解釋型語言:...
摘要:順序執行異步函數異步為帶來非阻塞等優勢的同時,同時也在一些場景下帶了不便,如順序執行異步函數,下面總結了一些常用的方法。 火于異步 1995年,當時最流行的瀏覽器——網景中開始運行 JavaScript (最初稱為 LiveScript)。 1996年,微軟發布了 JScript 兼容 JavaScript。隨著網景、微軟競爭而不斷的技術更新,在 2000年前后,JavaScript ...
摘要:所有的這些都是的功勞。默認為根據自己的指定的模板文件來生成特定的文件。最終在文件夾內會生成一個和文件。屬性值為文件所在的路徑名。默認值為不對生成的文件進行壓縮。選項的作用主要是針對多入口文件。不用說,按照不同文件的依賴關系來排序。 本文只在個人博客和 SegmentFault 社區個人專欄發表,轉載請注明出處 個人博客: https://zengxiaotao.github.io ...
摘要:還是老規矩,從易到難吧傳統的定時器,異步編程等。分配對象時,先是在空間中進行分配。內存泄漏內存泄漏是指程序中己動態分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重后果。 showImg(https://segmentfault.com/img/bVbwkad?w=1286&h=876); 網上參差不棄的面試題,本文由淺入深,讓你在...
摘要:還是老規矩,從易到難吧傳統的定時器,異步編程等。分配對象時,先是在空間中進行分配。內存泄漏內存泄漏是指程序中己動態分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重后果。 showImg(https://segmentfault.com/img/bVbwkad?w=1286&h=876); 網上參差不棄的面試題,本文由淺入深,讓你在...
閱讀 3274·2021-09-30 09:47
閱讀 2290·2021-09-10 10:51
閱讀 1889·2021-09-08 09:36
閱讀 2926·2019-08-30 12:56
閱讀 3027·2019-08-30 11:16
閱讀 2622·2019-08-29 16:40
閱讀 2994·2019-08-29 15:25
閱讀 1632·2019-08-29 11:02