Javascript自定義事件詳解
Javascript自定義事件,其本質(zhì)就是觀察者模式(又稱訂閱/發(fā)布模式),它的好處就是將綁定事件和觸發(fā)事件相互隔離開,并且可以動(dòng)態(tài)的添加、刪除事件。
下面通過實(shí)例,一步一步構(gòu)建一個(gè)具體的Javascript自定義事件對(duì)象。
如:我有一個(gè)action1函數(shù),我想每次在執(zhí)行完action1后,觸發(fā)另一個(gè)函數(shù)service1,那么代碼我們可以這么寫:
//服務(wù)service1 function service1(){ } //函數(shù)action1 function action1(){ //other things //then 啟動(dòng)service1 service1(); }
Good,但是現(xiàn)在想法變了,我想在action1完成后,不僅觸發(fā)service1,還要觸發(fā)service2和service3。
按照剛才的思路,在函數(shù)action1完成后,順帶加上它們就是了。
如下:
function service1(){} function service2(){} function service3(){} function action1(){ //other things service1(); service2(); service3(); }
但,想法又再次發(fā)生波動(dòng),在執(zhí)行完action1函數(shù)后,我突然想動(dòng)態(tài)添加一個(gè)service4,且,發(fā)現(xiàn)service2似乎毫無意義,我不想觸發(fā)了,怎么辦呢?
你可能會(huì)說去掉service2,然后在action1后面加入service4不就完了嗎?
但是,在真正的項(xiàng)目開發(fā)代碼日益劇增的情況下,談何容易,還要去找到相關(guān)代碼進(jìn)行操作。
那怎么辦呢?
初步想法,定義一個(gè)數(shù)組嘛(如:servicearray),用來管理所有的service。
當(dāng)action1執(zhí)行到末尾后,遍歷一遍這個(gè)數(shù)組函數(shù)(servicearray),就歐克了嘛。
且,倘若我們不想運(yùn)行service2了,便將它從這個(gè)數(shù)組中刪除就好了;倘若想再添加一個(gè)新的service,將其追加到servicearray數(shù)組中就好了。
如此nice,如下:
var servicearray = []; function service1(){} function service2(){} function service3(){} //將所有service添加到servicearray中 servicearray.push(service1); servicearray.push(service2); servicearray.push(service3); //del:用于刪除一個(gè)指定的service function del(arr, fn){ for(var i = 0; i < arr.length; i++){ if( arr[i] == fn ){ arr.splice(i,1); break; } } } //action1后,執(zhí)行所有的service function action1(){ //other things //遍歷serviceaary,執(zhí)行所有service函數(shù)。(servicearray在action1內(nèi)) for(var i =0; i < servicearray.length; i++){ servicearray[i](); } } //添加service4 function service4(){} servicearray.push(service4); //刪除service2 del(servicearray, service2);
上面代碼挺歐克的,但,復(fù)用性一點(diǎn)都不強(qiáng),且servicearray與action你中有我,我中有你,不好。我們再來優(yōu)化優(yōu)化。
代碼如下:
var servicearray = []; function service1(){} function service2(){} function service3(){} servicearray.push(service1); servicearray.push(service2); servicearray.push(service3); function del(arr, fn){ for(var i = 0; i < arr.length; i++){ if( arr[i] == fn ){ arr.splice(i,1); break; } } } //添加一個(gè)service4 function service4(){} servicearray.push(service4); //刪除一個(gè)service2 del(servicearray, service2); //添加一個(gè)觸發(fā)函數(shù)hanldeAction,分離action與service function hanldeAction(actionName,serviceArr){ if(typeof actionName === 'function'){ actionName(); for(var i =0; i < serviceArr.length; i++){ serviceArr[i](); } } } //執(zhí)行 handleAction(action1,servicearray);
上面的代碼和回調(diào)函數(shù)有異曲同工之處,因?yàn)槲覀兿脒_(dá)到的效果是在 action執(zhí)行完成之后,運(yùn)行一系列service嘛。
但,我現(xiàn)在改變想法了,我想在action執(zhí)行之前執(zhí)行一系列service呢,或者action中呢??磥淼酶膆anldeAction回調(diào)函數(shù)啊,這放在項(xiàng)目中反復(fù)修改,顯然不行。
所以,我們得讓其更強(qiáng)大才好。(我想讓它在什么地方執(zhí)行就執(zhí)行)
如下:
function service1(){} function service2(){} function service3(){} var servicearray = []; servicearray.push(service1); servicearray.push(service2); servicearray.push(service3); function del(arr, fn){ for(var i = 0; i < arr.length; i++){ if( arr[i] == fn ){ arr.splice(i,1); break; } } } //添加一個(gè)service4 function service4(){} servicearray.push(service4); //刪除一個(gè)service2 del(servicearray, service2); /* actionObj用于存儲(chǔ)所有action與service關(guān)聯(lián)的對(duì)象。 如:{ action1:[service1,service2], action2:[...] } */ var actionObj = {}; /* 修改代碼,增加一個(gè)actionName與serviceArr關(guān)聯(lián)事件。 如,action1關(guān)聯(lián)所有service,這樣再結(jié)合下方的trigger事件就完美了 Params: actionName --> actionObj的屬性 serviceArr --> 包含所有與actionName相關(guān)的service數(shù)組 */ function addAction(actionName, serviceArr){ if(typeof actionObj[actionName] === 'undefined' ){ actionObj[actionName] = []; } if(typeof serviceArr === 'object'){ actionObj[actionName].push(serviceArr); } } /* 修改代碼,增加一個(gè)觸發(fā)actionName事件 如,當(dāng)我想觸發(fā)action1中的所有service時(shí),調(diào)用trigger(action1)就OK啦 */ function trigger( actionName ){ var act = actionObj[actionName]; if(act instanceof Array){ for(var i = 0, len = act.length; i < len; i++){ for(var j =0, arrlen = act[i].length; j++){ ((act[i])[j])(); } } } }
上述代碼是可以,但,有個(gè)性能問題,addAction中添加到actionObj[actionName]中的是一個(gè)數(shù)組,其實(shí)完全可以將定義的servicearray數(shù)組(為了存儲(chǔ)不同的service而聲明的數(shù)組)移除,轉(zhuǎn)而將每個(gè)service直接push進(jìn)actionObj[actionName]聲明的數(shù)組中,這樣trigger事件效率也得到了提高,從原來的兩層for循環(huán)降到一層for循環(huán)。且,我們再加一個(gè)刪除service的方法remove。
整理代碼如下:
var actionObj = {}; //修改代碼,增加一個(gè)actionName與service函數(shù)直接關(guān)聯(lián)事件 function addAction(actionName, fn){ if(typeof actionObj[actionName] === 'undefined' ){ actionObj[actionName] = []; } if(typeof fn === 'function'){ actionObj[actionName].push(fn); } } //修改代碼,增加一個(gè)觸發(fā)actionName事件 function trigger( actionName ){ var actionarray = actionObj[actionName]; if(actionarray instanceof Array){ for(var i = 0, len = actionarray.length; i < len; i++){ if(typeof actionarray[i] === 'function'){ actionarray[i](); } } } } //修改代碼,增加一個(gè)刪除actionName中的service事件 function remove(actionName, fn){ var actionarray = actionObj[actionName]; if(typeof actionName === 'string' && actionarray instanceof Array){ if(typeof fn === 'function'){ //清除actionName中對(duì)應(yīng)的fn方法 for(var i=0, len = actionarray.length; i < len; i++){ if(actionarray[i] === fn){ actionObj[actionName].splice(i,1); } } } } }
上面的代碼好是好,action與service也互不影響,也完成了它的使命。
使命?
這就是我們一起編寫的自定義事件嘛。是不是很簡單。
哈哈哈,我也在代碼中用到設(shè)計(jì)模式了(觀察者模式)。
一鼓作氣,我們再來優(yōu)化下上面的代碼。有沒有注意,我們是使用的全局變量,在模塊化開發(fā)的大環(huán)境下,我們居然在用全局變量,豈不是污染命名空間嘛。再改改。
修改代碼如下:
var EventTarget = function(){ this.listener = {}; } EventTarget.prototype = { constructor:EventTarget, addAction: function(actionName, fn){ if(typeof actionName === 'string' && typeof fn === 'function'){ //如果不存在actionName,就新建一個(gè) if(typeof this.listener[actionName] === 'undefined'){ this.listener[actionName] = [fn]; } //否則,直接往相應(yīng)actinoName里面塞 else{ this.listener[actionName].push(fn); } } }, trigger: function(actionName){ var actionArray = this.listener[actionName]; //觸發(fā)一系列actionName里的函數(shù) if(actionArray instanceof Array){ for(var i = 0, len = actionArray.length; i < len; i++){ if(typeof actionArray[i] === 'function'){ actionArray[i](); } } } actionArray = null; }, remove: function(actionName, fn){ var actionArray = this.listener[actionName]; if(typeof actionName === 'string' && actionArray instanceof Array){ if(typeof fn === 'function'){ //清除actionName中對(duì)應(yīng)的fn方法 for(var i=0, len = actionArray.length; i < len; i++){ if(actionArray[i] === fn){ this.listener[actionName].splice(i,1); } } } } actionArray = null; } };
一個(gè)JavaScript自定義事件新鮮出爐。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
javascript實(shí)現(xiàn)的彈出層背景置灰-模擬(easyui dialog)
本文為大家介紹下使用javascript實(shí)現(xiàn)的彈出層背景置灰-模擬(easyui dialog) 具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下2013-12-12JS 實(shí)現(xiàn)banner圖片輪播效果(鼠標(biāo)事件)
js實(shí)現(xiàn)banner圖片輪播效果,通過鼠標(biāo)點(diǎn)擊左右可切換圖片,具體實(shí)現(xiàn)代碼大家參考下本文2017-08-08Express框架Router?Route?Layer對(duì)象使用示例詳解
這篇文章主要為大家介紹了Express框架Router?Route?Layer對(duì)象使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03全面解讀TypeScript和JavaScript的區(qū)別
TypeScript和JavaScript是目前項(xiàng)目開發(fā)中較為流行的兩種腳本語言, TypeScript是JavaScript的一個(gè)超集,JavaScript是一種輕量級(jí)的解釋性腳本語言,本文主要介紹了兩者區(qū)別,感興趣的可以了解一下2023-09-09JavaScript手寫一個(gè)前端存儲(chǔ)工具庫
在項(xiàng)目開發(fā)的過程中,為了減少提高性能,減少請求,開發(fā)者往往需要將一些不易改變的數(shù)據(jù)放入本地緩存中。本文就來用JavaScript手寫一個(gè)前端存儲(chǔ)工具庫,希望對(duì)大家有所幫助2023-02-02bootstrap導(dǎo)航欄、下拉菜單、表單的簡單應(yīng)用實(shí)例解析
這篇文章主要介紹了bootstrap導(dǎo)航欄、下拉菜單、表單的簡單應(yīng)用實(shí)例解析,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-01-01select下拉框插件jquery.editable-select詳解
本篇文章主要介紹了select下拉框插件jquery.editable-select。具有很好的參考價(jià)值,下面跟著小編一起來看下吧2017-01-01