




版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第使用TypeScript實(shí)現(xiàn)一個(gè)類(lèi)型安全的EventBus示例詳解目錄前言準(zhǔn)備工作目標(biāo)思路具體實(shí)現(xiàn)全部代碼后記
前言
隨著vue3的發(fā)布,TypeScript在國(guó)內(nèi)越來(lái)越流行,學(xué)習(xí)TypeScript也隨即變成了大勢(shì)所趨。本文就通過(guò)實(shí)現(xiàn)一個(gè)類(lèi)型安全的EventBus來(lái)練習(xí)TypeScript,希望對(duì)小伙伴們有所幫助。
準(zhǔn)備工作
生成一個(gè)TypeScript的基礎(chǔ)架子:
//創(chuàng)建目錄
mkdirts-event-buscdts-event-bus
//初始化工程
yarninit-y
//安裝typescript
yarnaddtypescript-D
//生成typescript配置文件
npxtsc--init
這樣一來(lái)我們就搭建好了一個(gè)TypeScript的基礎(chǔ)架子,為了方便我們后續(xù)的測(cè)試,我們需要下載ts-node,它可以讓我們?cè)诓痪幾gTypeScript代碼的情況下運(yùn)行TypeScript。
yarnaddts-node-D
目標(biāo)
基礎(chǔ)功能完備,包括注冊(cè),發(fā)布,取消訂閱三個(gè)核心功能。類(lèi)型安全,能約束我們輸入的參數(shù),并且有代碼提示。
思路
每一個(gè)Event都可以注冊(cè)多個(gè)處理函數(shù),我們用一個(gè)Set來(lái)保存這些處理函數(shù),再用一個(gè)Map來(lái)保存Event到對(duì)應(yīng)Set的映射,如圖所示:
具體實(shí)現(xiàn)
//定義泛型函數(shù)類(lèi)型
typeHandlerT=any=(val:T)=void;
classEventBusEventsextendsRecordstring,any{
/**保存key=set映射*/
privatemap:Mapstring,SetHandler=newMap();
onEventNameextendskeyofEvents(
name:EventName,
handler:HandlerEvents[EventName]
letset:SetHandlerEvents[EventName]|undefined=this.map.get(
nameasstring
if(!set){
set=newSet();
this.map.set(nameasstring,set);
set.add(handler);
}
這里我們分成邏輯和類(lèi)型兩方面來(lái)講
邏輯方面,我們初始化了一個(gè)空的Map,然后當(dāng)調(diào)用on用來(lái)注冊(cè)事件的時(shí)候,先去根據(jù)EventName來(lái)找有沒(méi)有對(duì)應(yīng)的Set,沒(méi)有就創(chuàng)建一個(gè),并且把事件添加到Set中,這一部分的代碼相當(dāng)簡(jiǎn)單,實(shí)現(xiàn)起來(lái)也沒(méi)什么難度。
類(lèi)型方面,我們將EventBus定義為一個(gè)泛型類(lèi),并約束泛型為EventsextendsRecordstring,any,這樣就約束了傳入的泛型參數(shù)必須是一個(gè)對(duì)象類(lèi)型,例如:
typeEvents={
foo:number;
bar:string;
}
我們可以通過(guò)這個(gè)類(lèi)型來(lái)獲取key對(duì)應(yīng)value的類(lèi)型
//number;
typeValueTypeOfFoo=Events['foo']
進(jìn)而可以獲取foo事件對(duì)應(yīng)的handler函數(shù)的類(lèi)型,即:
//(val:number)=void;
typeHandlerOfFoo=HandlerEvents['foo']
我們又將on方法設(shè)置為泛型函數(shù),同時(shí)約束EventNameextendskeyofEvents,這樣一來(lái)Events[EventName]就是對(duì)應(yīng)值的類(lèi)型,HandlerEvents[EventName]就是處理函數(shù)的類(lèi)型。通過(guò)這樣的方式我們實(shí)現(xiàn)了一個(gè)類(lèi)型安全的on方法。
接著我們編寫(xiě)一段代碼測(cè)試一下
可以看到,我們?cè)趘scode中編寫(xiě)代碼的時(shí)候,編輯器能給我們代碼提示了。
我們鍵入handler函數(shù),編輯器也會(huì)提醒我們val是一個(gè)string類(lèi)型。
當(dāng)我們傳的參數(shù)不合法的時(shí)候,TypeScript也會(huì)給我們警告
接下來(lái)我們依葫蘆畫(huà)瓢實(shí)現(xiàn)emit函數(shù)。
classEventBusEventsextendsRecordstring,any{
...otherscode
/**觸發(fā)事件*/
emitEventNameextendskeyofEvents(
name:EventName,
value:Events[EventName]
constset:SetHandlerEvents[EventName]|undefined=this.map.get(
nameasstring
if(!set)return;
constcopied=[...set];
copied.forEach((fn)=fn(value));
}
先找到EventName對(duì)應(yīng)的Set,如果有就取出并依次執(zhí)行。這里的邏輯也相當(dāng)簡(jiǎn)單,我們編寫(xiě)代碼測(cè)試一下
constbus=newEventBus{
foo:string;
bar:number;
bus.on("foo",(val)={
console.log(val);
//輸出hello
bus.emit("foo","hello");
我們?cè)诮K端運(yùn)行npxts-node./index.ts,輸出hello,說(shuō)明我們的程序已經(jīng)生效。
接下來(lái)我們實(shí)現(xiàn)取消訂閱的功能。
{
offEventNameextendskeyofEvents(
name:EventName,
handler:HandlerEvents[EventName]
):void{
//什么都不傳,則清除所有事件
if(!name){
this.map.clear();
return;
//只傳名字,則清除同名事件
if(!handler){
this.map.delete(nameasstring);
return;
//name和handler都傳了,則清除指定handler
consthandlers:SetHandlerEvents[EventName]|undefined=this.map.get(
nameasstring
if(!handlers){
return;
handlers.delete(handler);
}
取消訂閱我們這樣設(shè)計(jì),它傳入0至2個(gè)參數(shù),什么都不傳代表清除所有事件,只傳一個(gè)參數(shù)代表清除同名事件,傳兩個(gè)參數(shù)代表只清除該事件指定的處理函數(shù),所以它的兩個(gè)參數(shù)都是可選的,實(shí)現(xiàn)的邏輯也非常簡(jiǎn)單,我們這里不多贅述。
我們編寫(xiě)一段測(cè)試代碼看下效果
constbus=newEventBus{
foo:string;
bar:number;
//測(cè)試傳2個(gè)參數(shù)的情況
consthandlerFoo1=(val:string)={
console.log("2個(gè)參數(shù)handlerFoo1=",val);
bus.on("foo",handlerFoo1);
bus.emit("foo","hello");
//打印2個(gè)參數(shù)handlerFoo1=hello
bus.off("foo",handlerFoo1);
bus.emit("foo","hello");
//什么都沒(méi)打印
//測(cè)試傳1個(gè)參數(shù)的情況
consthandlerFoo2=(val:string)={
console.log("1個(gè)參數(shù)handlerFoo2=",val);
consthandlerFoo3=(val:string)={
console.log("1個(gè)參數(shù)handlerFoo3=",val);
bus.on("foo",handlerFoo2);
bus.on("foo",handlerFoo3);
bus.emit("foo","hello");
//打印1個(gè)參數(shù)handlerFoo2=hello
//打印1個(gè)參數(shù)handlerFoo3=hello
bus.off("foo");
bus.emit("foo","hello");
//什么都沒(méi)輸出
//測(cè)試傳0個(gè)參數(shù)的情況
consthandlerFoo4=(val:string)={
console.log("0個(gè)參數(shù)handlerFoo4=",val);
consthandlerBar1=(val:number)={
console.log("0個(gè)參數(shù)handlerBar1=",val);
bus.on("foo",handlerFoo4);
bus.on("bar",handlerBar1);
bus.emit("foo","hello");
bus.emit("bar",123);
//打印1個(gè)參數(shù)handlerFoo4=hello
//打印1個(gè)參數(shù)handlerBar1=123
bus.off();
bus.emit("foo","hello");
bus.emit("bar",123);
//什么都沒(méi)輸出
從測(cè)試結(jié)果來(lái)看,我們的off方法功能也沒(méi)問(wèn)題,這樣就完成了我們的EventBus。
此外,我們還可以給我們的方法加上注釋?zhuān)@樣在我們鼠標(biāo)移到api上方和我們輸入?yún)?shù)的時(shí)候,編輯器就會(huì)有提示。
/**
*訂閱事件
*@paramname事件名
*@paramhandler事件處理函數(shù)
onEventNameextendskeyofEvents(
name:EventName,
handler:HandlerEvents[EventName]
letset:SetHandlerEvents[EventName]|undefined=this.map.get(
nameasstring
if(!set){
set=newSet();
this.map.set(nameasstring,set);
set.add(handler);
}
可以看到,編輯器給我們提供了很好的提示,極大方便了我們的編碼。
我們還可以用函數(shù)重載來(lái)改進(jìn)我們的off方法,以獲得更友好的提示
{
*清除所有事件
off():void;
*清除同名事件
*@paramname事件名
offEventNameextendskeyofEvents(name:EventName):void;
*清除指定事件
*@paramname事件名
*@paramhandler事件處理函數(shù)
offEventNameextendskeyofEvents(
name:EventName,
handler:HandlerEvents[EventName]
):void;
offEventNameextendskeyofEvents(
name:EventName,
handler:HandlerEvents[EventName]
):void{
//什么都不傳,則清除所有事件
if(!name){
this.map.clear();
return;
//只傳名字,則清除同名事件
if(!handler){
this.map.delete(nameasstring);
return;
//name和handler都傳了,則清除指定handler
consthandlers:SetHandlerEvents[EventName]|undefined=this.map.get(
nameasstring
if(!handlers){
return;
handlers.delete(handler);
改造前的提示:
改造后的提示:
至此,我們就完成了一個(gè)功能完備,類(lèi)型安全的EventBus了。
全部代碼
typeHandlerT=any=(val:T)=void;
classEventBusEventsextendsRecordstring,any{
privatemap:Mapstring,SetHandler=newMap();
*訂閱事件
*@paramname事件名
*@paramhandler事件處理函數(shù)
onEventNameextendskeyofEvents(
name:EventName,
handler:HandlerEvents[EventName]
letset:SetHandlerEvents[EventName]|undefined=this.map.get(
nameasstring
if(!set){
set=newSet();
this.map.set(nameasstring,set);
set.add(handler);
*觸發(fā)事件
*@paramname事件名
*@paramhandler事件處理函數(shù)
emitEventNameextendskeyofEvents(
name:EventName,
value:Events[EventName]
constset:SetHandlerEvents[EventName]|undefined=this.map.get(
nameasstring
if(!set)return;
constcopied=[...set];
copied.forEach((fn)=fn(value));
*清除所有事件
off():void;
*清除同名事件
*@paramname事件名
offEventNameextendskeyofEvents(name:EventName):void;
*清除指定事件
*@paramname事件名
*@paramhandler處理函數(shù)
offEventNameextendskeyofEvents(
name:EventName,
handler:HandlerEvents[EventName]
):void;
offEventNameextendskeyofEvents(
name:EventName,
handler:HandlerEvents[EventName]
):void{
//什么都不傳,則清除所有事件
if(!name){
this.map.clear();
return;
//只傳名字,則清除同名事件
if(!handler){
this.map.delete(nameasstring);
return;
//name和handler都傳了,則清除指定handler
consthandlers:SetHandlerEvents[EventName]|undefined=this.map.get(
nameasstring
if(!handlers){
return;
handlers.delete(handler);
constbus=newEventBus{
foo:string;
bar:number;
//測(cè)試傳2個(gè)參數(shù)的情況
consthandlerFoo1=(val:string)={
console.log("2個(gè)參數(shù)handlerFoo1=",val);
bus.on("foo",handlerFoo1);
bus.emit("foo","hello");
//打印2個(gè)參數(shù)handlerFoo1=hello
bus.off("foo",handlerFoo1);
bus.emit("foo","hello");
//什么都沒(méi)打印
//測(cè)試傳1個(gè)參數(shù)的情況
consthandlerFoo2=(val:string)={
console.log("1個(gè)參數(shù)handlerFoo2=",val);
con
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 文化會(huì)展服務(wù)相關(guān)主題名稱(chēng)續(xù)考核試卷
- 公路工程現(xiàn)場(chǎng)安全試題及答案
- 金屬工具的回收再利用與環(huán)保處理考核試卷
- 運(yùn)動(dòng)裝備租賃服務(wù)創(chuàng)新理念考核試卷
- 數(shù)據(jù)庫(kù)正則化方法試題及答案
- 數(shù)據(jù)庫(kù)實(shí)踐中的應(yīng)試者準(zhǔn)備事項(xiàng)總結(jié)試題及答案
- 嵌入式系統(tǒng)只為你知的試題及答案
- 探索深邃的2025年行政組織理論考試試題及答案
- 計(jì)算機(jī)四級(jí)軟件測(cè)試考試考綱及試題及答案
- 外資公司薪酬管理制度
- 法院強(qiáng)制執(zhí)行申請(qǐng)書(shū)標(biāo)準(zhǔn)范文
- 索緒爾“語(yǔ)言”和“言語(yǔ)”概念研究
- 2024年地板行業(yè)分析報(bào)告及未來(lái)發(fā)展趨勢(shì)
- 2020-心肌梗死后心力衰竭防治專(zhuān)家共識(shí)
- 經(jīng)典成語(yǔ)故事望梅止渴
- 二級(jí)公立醫(yī)院績(jī)效考核三級(jí)手術(shù)目錄(2020版)
- 2023年遼寧省普通高等學(xué)校招生錄取普通類(lèi)本科批(物理學(xué)科類(lèi))投檔最低分
- 電裝 集團(tuán)禁限用工藝-2013版
- 燃?xì)庑袠I(yè)數(shù)字化轉(zhuǎn)型與智能化
- VDA6.3檢查要求與證據(jù)清單(VDA6.3檢查表)
- 醫(yī)院藥品集中帶量采購(gòu)和使用工作管理辦法
評(píng)論
0/150
提交評(píng)論