談一談JS消息機(jī)制和事件機(jī)制的理解
消息/事件機(jī)制是幾乎所有開(kāi)發(fā)語(yǔ)言都有的機(jī)制,并不是deviceone的獨(dú)創(chuàng),在某些語(yǔ)言稱(chēng)之為消息(Event),有些地方稱(chēng)之為(Message). 其實(shí)原理是類(lèi)似的,只不過(guò)有些實(shí)現(xiàn)的方式要復(fù)雜一點(diǎn)。我們deviceone統(tǒng)一就叫消息.
消息基礎(chǔ)概念
還有一些初學(xué)者不太熟悉這個(gè)機(jī)制,我們先簡(jiǎn)單介紹一些基礎(chǔ)概念,如果熟悉的人可以跳過(guò)這個(gè)部分。
一個(gè)/條消息可以理解為是一個(gè)數(shù)據(jù)結(jié)構(gòu),包含以下幾個(gè)基本部分:
1.消息源:就是消息的來(lái)源,發(fā)出這個(gè)消息的對(duì)象
2.消息名:就是消息的唯一標(biāo)示
3.消息數(shù)據(jù):消息發(fā)出后附帶的數(shù)據(jù),有可能數(shù)據(jù)是空
消息從種類(lèi)上又可以分為2種:
1.系統(tǒng)消息:由操作系統(tǒng)或deviceone系統(tǒng)發(fā)送出來(lái)的消息,消息的名稱(chēng)是固定的。
2.自定義消息:由開(kāi)發(fā)者自己定義,自己發(fā)送出來(lái)的消息,消息的名字是隨意的,可以任意定義。
舉例說(shuō)明:
比如用戶(hù)點(diǎn)擊一個(gè)do_Button按鈕,就會(huì)觸發(fā)一個(gè)系統(tǒng)消息,包含3個(gè)部分:
1.消息源:用戶(hù)點(diǎn)中的button對(duì)象
2.消息名:touch
3.消息數(shù)據(jù):這個(gè)消息沒(méi)有附帶數(shù)據(jù)
比如用戶(hù)通過(guò)do_Button按鈕觸發(fā)一個(gè)自定義事件,包含3個(gè)部分:
1.消息源: button對(duì)象
2.消息名:用戶(hù)隨便定義,叫aaa,bbb,ccc都可以
3.消息數(shù)據(jù):附帶的數(shù)據(jù)由觸發(fā)消息的時(shí)候設(shè)定
發(fā)布/訂閱模式
發(fā)布/訂閱模式是最常用的設(shè)計(jì)模式之一,是消息機(jī)制的核心,其特點(diǎn)就是降低耦合度,讓二個(gè)獨(dú)立的對(duì)象不互相依賴(lài)。簡(jiǎn)單介紹一下,熟悉的同學(xué)可以跳過(guò)。
我們先從現(xiàn)實(shí)的一個(gè)簡(jiǎn)單例子來(lái)說(shuō)明這個(gè)問(wèn)題,參考下圖:
從這個(gè)圖我們可以看出
1.消費(fèi)者和出版社互相不認(rèn)識(shí),消費(fèi)者不需要了解他想要的雜志是具體哪家出版社出的;出版社也不需要了解具體是哪個(gè)人定了他們出版社發(fā)行的書(shū)。
2.消費(fèi)者和出版社必須都認(rèn)識(shí)郵局。
3.消費(fèi)者需要告訴郵局消費(fèi)者的名字地址以及想要訂閱的雜志名字
4.可以多個(gè)消費(fèi)者訂閱同一本雜志
5.郵局拿到雜志后,會(huì)一一通知消費(fèi)者,通知的時(shí)候同時(shí)把雜志送到消費(fèi)者手里。
看完上面現(xiàn)實(shí)例子,我們?cè)賮?lái)看抽象的描述會(huì)更清晰一點(diǎn),看下圖:
和上面的實(shí)際例子描述一一對(duì)應(yīng):
1.系統(tǒng)/開(kāi)發(fā)者和函數(shù)對(duì)象互相不依賴(lài),系統(tǒng)/開(kāi)發(fā)者只管觸發(fā)一個(gè)消息,并不關(guān)心誰(shuí)去接受
2.系統(tǒng)/開(kāi)發(fā)者和函數(shù)對(duì)象必須能獲取到消息源對(duì)象
3.函數(shù)對(duì)象訂閱消息的時(shí)候需要標(biāo)示消息的名稱(chēng)和函數(shù)對(duì)象的引用
4.可以多個(gè)函數(shù)對(duì)象訂閱同一個(gè)消息源同一名字的消息
5.消息源觸發(fā)消息會(huì)一一通知所有訂閱者,并把data數(shù)據(jù)傳遞到回調(diào)函數(shù)對(duì)象
看完抽象的描述,我們最后來(lái)看實(shí)際的deviceone開(kāi)發(fā)的例子,還是以do_Button為例子。
1. 當(dāng)用戶(hù)點(diǎn)擊一個(gè)button,觸摸到的時(shí)候,系統(tǒng)會(huì)獲取到button這個(gè)對(duì)象作為消息源,fire一個(gè)”touch”消息,任何訂閱了”touch”消息的函數(shù)對(duì)象都會(huì)接收到這個(gè)消息并引起函數(shù)的執(zhí)行。
//獲取button對(duì)象 var btn_hello = ui("btn_hello"); //定義函數(shù)對(duì)象 function f(){ //當(dāng)btn_hello這個(gè)按鈕接收到手指點(diǎn)擊就會(huì)執(zhí)行下面的代碼 deviceone.print("f 函數(shù)接收到點(diǎn)擊觸發(fā)消息") } function f(){ //當(dāng)btn_hello這個(gè)按鈕接收到手指點(diǎn)擊就會(huì)執(zhí)行下面的代碼 deviceone.print("f 函數(shù)接收到點(diǎn)擊觸發(fā)消息") } //f,f訂閱button的touch消息 btn_hello.on("touch",f); btn_hello.on("touch",f);
2. 我們可以為button對(duì)象定義2個(gè)自定義的消息”message1”和”message2”,分別有2個(gè)函數(shù)對(duì)象訂閱這2個(gè)消息。但是最后要觸發(fā)這個(gè)消息必須是開(kāi)發(fā)者通過(guò)調(diào)用fire函數(shù)才能觸發(fā),這就是和系統(tǒng)消息的區(qū)別。
//獲取button對(duì)象 var btn_hello = ui("btn_hello"); //定義函數(shù)對(duì)象 function f(d){ //當(dāng)btn_hello這個(gè)按鈕接收到開(kāi)發(fā)者觸發(fā)的消息message就會(huì)執(zhí)行下面的代碼 deviceone.print("f 函數(shù)接收到message消息,消息的數(shù)據(jù)是:"+d) } function f(d){ //當(dāng)btn_hello這個(gè)按鈕接收到開(kāi)發(fā)者觸發(fā)的消息message就會(huì)執(zhí)行下面的代碼 deviceone.print("f 函數(shù)接收到message消息,消息的數(shù)據(jù)是:"+d) } //f,f訂閱button的touch消息 btn_hello.on("message",f); btn_hello.on("message",f); //觸發(fā)消息 btn_hello.fire("message","data"); btn_hello.fire("message","data");
看到這里,你肯定會(huì)奇怪,為什么我們要在button上自定義對(duì)象?這有神馬意義?其實(shí)確實(shí)沒(méi)有意義也沒(méi)有必要,這里只是拿button舉例子,在常規(guī)的開(kāi)發(fā)中,基本不會(huì)這么用。
消息的使用
前面講了這么多,現(xiàn)在才是deviceone消息的使用。使用其實(shí)很簡(jiǎn)單,上面的例子基本說(shuō)明的了系統(tǒng)事件和自定義事件的使用方法。
有幾個(gè)概念再說(shuō)明一下
1.deviceone的所有對(duì)象,包括UI,MM,SM對(duì)象都可以是消息源
// SM對(duì)象可以是消息源 var page = sm("do_Page"); page.on("loaded",function()){ // 這個(gè)是page對(duì)象的系統(tǒng)消息,這個(gè)消息不需要手動(dòng)觸發(fā),系統(tǒng)會(huì)自動(dòng)觸發(fā) } page.on("message",function(d)){ // 這個(gè)是page對(duì)象的自定義消息 } page.fire("message","data"); // MM對(duì)象可以是消息源 var http = mm("do_Http"); http.on("result",function()){ // 這個(gè)是http對(duì)象的系統(tǒng)消息,這個(gè)消息不需要手動(dòng)觸發(fā),接受到http服務(wù)端的反饋后會(huì)自動(dòng)觸發(fā) } http.on("message",function(d)){ // 這個(gè)是http對(duì)象的自定義消息 } http.fire("message","data"); //UI對(duì)象可以是消息源 var alayout = ui("alayout_id"); alayout.on("touch",function()){ // 這個(gè)是alayout對(duì)象的系統(tǒng)消息,這個(gè)消息不需要手動(dòng)觸發(fā),手機(jī)點(diǎn)擊就會(huì)觸發(fā) } alayout.on("message",function(d)){ // 這個(gè)是alayout對(duì)象的自定義消息 } alayout.fire("message","data");
2.消息源對(duì)象有作用域,所以訂閱和觸發(fā)的消息源必須是是一個(gè)作用域的同一個(gè)對(duì)象。這里結(jié)合數(shù)據(jù)分享和數(shù)據(jù)傳遞文檔來(lái)理解。
看以下的例子,test1.ui和test2.ui有可能在一個(gè)page作用域,也有可能不在一個(gè)作業(yè)域,只有在一個(gè)作用域fire的消息才能正確送達(dá)回調(diào)函數(shù)。
判斷是否一樣,可以通過(guò)打印page的地址 page.getAddress().
//在test.ui.js里訂閱消息 var page = sm("do_Page"); deviceone.print(page.getAddress()); page.on("message",function(d)){ deviceone.print(d); } //在test.ui.js觸發(fā)消息 var page = sm("do_Page"); deviceone.print(page.getAddress()); page.fire("message","data");
如果不在同一page作用域,則可以把消息訂閱在2個(gè)page都能共享到的app作用域
上面的代碼改成:
//在test.ui.js里訂閱消息 var app = sm("do_App"); app.on("message",function(d)){ deviceone.print(d); } //在test.ui.js觸發(fā)消息 var app = sm("do_App"); app.fire("message","data");
3.同樣的函數(shù)對(duì)象可以重復(fù)訂閱一個(gè)對(duì)象源的消息,觸發(fā)消息的時(shí)候會(huì)使函數(shù)執(zhí)行多次,這是初學(xué)者經(jīng)常犯的錯(cuò)誤。
var page = sm("do_Page"); var count = ; function f(){ deviceone.print("執(zhí)行次數(shù)"+(count++)); } page.on("message",f); page.on("message",f); page.fire("message");
看上面的例子,如果執(zhí)行的話,會(huì)打印2此,因?yàn)橛嗛喠?次,或許你會(huì)說(shuō)誰(shuí)會(huì)寫(xiě)這樣的代碼?實(shí)際情況肯定沒(méi)有這么容易看出來(lái)執(zhí)行了重復(fù)的on函數(shù),實(shí)際情況經(jīng)常是比如在點(diǎn)擊事件里執(zhí)行on函數(shù),每點(diǎn)擊一下按鈕,就重復(fù)訂閱一次。
4.消息的訂閱一定要在消息的觸發(fā)之前,這是初學(xué)者經(jīng)常犯的錯(cuò)誤。
var page = sm("do_Page"); var count = ; function f(){ deviceone.print("執(zhí)行次數(shù)"+(count++)); } page.fire("message"); page.on("message",f);
看上面的例子,如果執(zhí)行的話,會(huì)沒(méi)有效果,或許你會(huì)說(shuō)誰(shuí)會(huì)寫(xiě)這樣的代碼?實(shí)際情況肯定沒(méi)有這么容易看出來(lái)順序反了,實(shí)際情況經(jīng)常是比如on函數(shù)執(zhí)行在某一個(gè)函數(shù)的回調(diào)函數(shù)里,你無(wú)法確定回調(diào)函數(shù)啥時(shí)候執(zhí)行,是否是在fire之前執(zhí)行。一般碰到這種情況可以加幾個(gè)deviceone.print打印一下看看是on先執(zhí)行還是fire先執(zhí)行。
5.有訂閱就有取消訂閱,取消訂閱是off函數(shù),之所以很少用,是因?yàn)閏losePage的時(shí)候會(huì)自動(dòng)把當(dāng)前page作用域訂閱的消息全部釋放。
但是如果消息訂閱在app作用域,就要注意,可能需要手動(dòng)去取消訂閱。否則就會(huì)出現(xiàn)觸發(fā)消息的時(shí)候會(huì)使函數(shù)執(zhí)行多次的問(wèn)題。
var page = sm("do_Page"); var count = ; function f(){ deviceone.print("執(zhí)行次數(shù)"+(count++)); } page.on("message",f); page.fire("message"); .page.off("message"); page.fire("message");
看上面的例子,打印只會(huì)執(zhí)行一次,因?yàn)閒ire一次后就取消訂閱了。
相關(guān)文章
JavaScript中各種時(shí)間轉(zhuǎn)換問(wèn)題詳解(YYYY-MM-DD、時(shí)間戳、中國(guó)標(biāo)準(zhǔn)時(shí)間)
在某些場(chǎng)景下,需要將時(shí)間轉(zhuǎn)換為字符串進(jìn)行展示或傳遞,下面這篇文章主要給大家介紹了關(guān)于JavaScript中各種時(shí)間轉(zhuǎn)換問(wèn)題(YYYY-MM-DD、時(shí)間戳、中國(guó)標(biāo)準(zhǔn)時(shí)間)的相關(guān)資料,需要的朋友可以參考下2024-02-02javascript實(shí)現(xiàn)數(shù)組內(nèi)值索引隨機(jī)化及創(chuàng)建隨機(jī)數(shù)組的方法
這篇文章主要介紹了javascript實(shí)現(xiàn)數(shù)組內(nèi)值索引隨機(jī)化及創(chuàng)建隨機(jī)數(shù)組的方法,涉及javascript數(shù)組索引及隨機(jī)數(shù)的相關(guān)實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08javascript:json數(shù)據(jù)的頁(yè)面綁定示例代碼
本篇文章主要是對(duì)javascript:json數(shù)據(jù)的頁(yè)面綁定示例代碼進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01javascript:void(0)的真正含義實(shí)例分析
void操作符解釋2008-08-08js添加千分位的實(shí)現(xiàn)代碼(超簡(jiǎn)單)
下面小編就為大家?guī)?lái)一篇js添加千分位的實(shí)現(xiàn)代碼(超簡(jiǎn)單)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-08-08利用js實(shí)現(xiàn)前臺(tái)動(dòng)態(tài)添加文本框,后臺(tái)獲取文本框內(nèi)容(示例代碼)
這篇文章主要是對(duì)利用js實(shí)現(xiàn)前臺(tái)動(dòng)態(tài)添加文本框,后臺(tái)獲取文本框內(nèi)容的示例代碼進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-11-11javascript中的return和閉包函數(shù)淺析
這篇文章主要介紹了javascript中的return和閉包函數(shù)淺析,至少可以讓你搞懂那么多()是什么意思,需要的朋友可以參考下2014-06-06JS實(shí)現(xiàn)定時(shí)自動(dòng)關(guān)閉DIV層提示框的方法
這篇文章主要介紹了JS實(shí)現(xiàn)定時(shí)自動(dòng)關(guān)閉DIV層提示框的方法,可實(shí)現(xiàn)加載時(shí)載入js代碼控制div層提示框自動(dòng)關(guān)閉的效果,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-05-05Bootstrap popover 實(shí)現(xiàn)鼠標(biāo)移入移除顯示隱藏功能方法
下面小編就為大家分享一篇Bootstrap popover 實(shí)現(xiàn)鼠標(biāo)移入移除顯示隱藏功能方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01