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