JS消息机制与事件机制详解:从基础概念到发布/订阅模式

作者:流萤时间:2026-10-30 05:27:27浏览:408

本文系统解析JavaScript中的消息与事件机制,阐述其作为跨语言通用机制的核心原理。内容涵盖消息的三大要素(源、名、数据)、系统消息与自定义消息的区别,并深入剖析发布/订阅模式在解耦对象间的作用。结合deviceone框架实例,详细说明消息的订阅、触发、作用域管理及常见错误处理,帮助开发者掌握高效的消息通信技巧。

JS消息机制概述

消息或事件机制是各类开发语言中普遍存在的基础架构,并非deviceone所独有。在不同语境下,该机制可能被称为Event或Message,尽管部分语言的具体实现方式较为复杂,但其核心原理相通。在deviceone中,统一采用“消息”这一术语进行描述。

消息基础概念

消息基础概念解析

针对对该机制尚不熟悉的初学者,此处简要介绍基础概念;熟悉者可略过。一条消息本质上是一个数据结构,主要包含以下基本组成部分:

1. 消息源:即消息的发出者,指代产生该消息的具体对象。

2. 消息名:作为消息的唯一标识符。

3. 消息数据:消息发出时附带的数据负载,该部分数据可能为空。

从分类角度来看,消息主要划分为两种类型:

1. 系统消息:由操作系统或deviceone底层系统自动发送的消息,其名称是固定且预定义的。

2. 自定义消息:由开发者自行定义并发送的消息,其名称完全自由,可根据需要任意指定。

以下通过具体示例进行说明:

例如,当用户点击一个do_Button按钮时,会触发一个系统消息,该消息包含三个部分:

1. 消息源:用户点击的那个button对象。

2. 消息名:touch。

3. 消息数据:此场景下未附带额外数据。

再如,用户通过do_Button按钮触发一个自定义事件,该事件同样包含三个部分:

1. 消息源:button对象。

2. 消息名:由用户随意定义,如aaa、bbb、ccc等均可。

3. 消息数据:具体数据内容在触发消息时由开发者设定。

发布/订阅模式详解

发布/订阅模式

发布/订阅模式是广泛使用的核心设计模式之一,也是消息机制的基石。其主要优势在于降低耦合度,使两个独立对象之间无需直接依赖。此处简要介绍,熟悉该模式的读者可跳过。

首先通过一个现实生活中的简单案例来说明,请参考下图:

JS消息机制概述

从该图中我们可以观察到以下关键点:

1. 消费者与出版社互不相识,消费者无需知晓其订阅的杂志具体由哪家出版社发行;同样,出版社也不必了解具体是哪位个人订阅了其发行的书籍。

2. 消费者和出版社必须共同认识邮局,即消息传递的中枢。

3. 消费者需向邮局提供姓名、地址以及想要订阅的杂志名称。

4. 多个消费者可以订阅同一本杂志。

5. 邮局在收到杂志后,会逐一通知所有订阅者,并在通知时将杂志送达消费者手中。

理解上述现实案例后,再看抽象描述会更加清晰,请参考下图:

JS消息机制概述

抽象描述与现实案例一一对应:

1. 系统/开发者与函数对象之间互不依赖,系统或开发者只需触发消息,无需关心由谁接收。

2. 系统/开发者与函数对象必须能够获取到消息源对象。

3. 函数对象在订阅消息时,需标示消息名称及函数对象自身的引用。

4. 多个函数对象可以订阅来自同一消息源且名称相同的消息。

5. 消息源触发消息时,会逐一通知所有订阅者,并将data数据传递给回调函数对象。

理解抽象描述后,最后来看deviceone开发中的实际例子,仍以do_Button为例。

1. 当用户点击按钮时,系统会获取button对象作为消息源,并fire一个“touch”消息。任何订阅了“touch”消息的函数对象都会接收到该消息并触发函数执行。


//获取button对象
var btn_hello = ui("btn_hello");
//定义函数对象
function f(){
//当btn_hello这个按钮接收到手指点击就会执行下面的代码
deviceone.print("f 函数接收到点击触发消息")
}
function f(){
//当btn_hello这个按钮接收到手指点击就会执行下面的代码
deviceone.print("f 函数接收到点击触发消息")
}
//f,f订阅button的touch消息
btn_hello.on("touch",f);
btn_hello.on("touch",f);

2. 我们可以为button对象定义两个自定义消息“message1”和“message2”,分别由两个函数对象订阅。但触发这些消息必须由开发者调用fire函数才能执行,这正是其与系统消息的区别所在。


//获取button对象
var btn_hello = ui("btn_hello");
//定义函数对象
function f(d){
//当btn_hello这个按钮接收到开发者触发的消息message就会执行下面的代码
deviceone.print("f 函数接收到message消息,消息的数据是:"+d)
}
function f(d){
//当btn_hello这个按钮接收到开发者触发的消息message就会执行下面的代码
deviceone.print("f 函数接收到message消息,消息的数据是:"+d)
}
//f,f订阅button的touch消息
btn_hello.on("message",f);
btn_hello.on("message",f);
//触发消息
btn_hello.fire("message","data");
btn_hello.fire("message","data");

看到这里,读者可能会疑惑:为何要在button上自定义对象?这有何意义?实际上,这并无实际必要,此处仅以button为例。在常规开发中,极少采用这种用法。

deviceone消息使用指南

消息的使用

前文已详述诸多概念,现在正式介绍deviceone中消息的具体使用方法。上述示例已基本阐明了系统事件和自定义事件的使用流程。

此处再补充说明几个关键概念:

1. deviceone中的所有对象,包括UI、MM、SM对象,均可作为消息源。


// SM对象可以是消息源
var page = sm("do_Page");
page.on("loaded",function()){
// 这个是page对象的系统消息,这个消息不需要手动触发,系统会自动触发
}
page.on("message",function(d)){
// 这个是page对象的自定义消息
}
page.fire("message","data");
// MM对象可以是消息源
var http = mm("do_Http");
http.on("result",function()){
// 这个是http对象的系统消息,这个消息不需要手动触发,接受到http服务端的反馈后会自动触发
}
http.on("message",function(d)){
// 这个是http对象的自定义消息
}
http.fire("message","data");
//UI对象可以是消息源
var alayout = ui("alayout_id");
alayout.on("touch",function()){
// 这个是alayout对象的系统消息,这个消息不需要手动触发,手机点击就会触发
}
alayout.on("message",function(d)){
// 这个是alayout对象的自定义消息
}
alayout.fire("message","data");

2. 消息源对象具有作用域限制,因此订阅和触发的消息源必须是同一作用域内的同一对象。建议结合数据分享和数据传递文档进行理解。

参考以下示例,test1.ui和test2.ui可能处于同一page作用域,也可能不在同一作业域。只有当fire的消息处于同一作用域时,才能正确送达回调函数。

判断是否处于同一作用域,可通过打印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触发消息
var page = sm("do_Page");
deviceone.print(page.getAddress());
page.fire("message","data");

若不在同一page作用域,则可将消息订阅在两个page均可共享的app作用域中。 修改后的代码如下:


//在test.ui.js里订阅消息
var app = sm("do_App");
app.on("message",function(d)){
deviceone.print(d);
}
//在test.ui.js触发消息
var app = sm("do_App");
app.fire("message","data");

3. 同一函数对象若重复订阅同一对象源的消息,触发消息时会导致该函数执行多次,这是初学者常犯的错误。


var page = sm("do_Page");
var count = ;
function f(){
deviceone.print("执行次数"+(count++));
}
page.on("message",f);
page.on("message",f);
page.fire("message");

参考上述示例,若执行代码,会打印两次结果,因为订阅了两次。或许有人质疑谁会写此类代码?实际情况中,重复订阅往往不易察觉。例如,在点击事件中执行on函数,每次点击按钮便重复订阅一次,导致函数多次执行。

4. 消息的订阅必须在消息触发之前完成,这也是初学者常犯的错误。


var page = sm("do_Page");
var count = ;
function f(){
deviceone.print("执行次数"+(count++));
}
page.fire("message");
page.on("message",f);

参考上述示例,若执行代码,将无任何效果。或许有人质疑谁会写此类代码?实际情况中,顺序颠倒往往不易察觉。例如,on函数执行于某函数的回调函数内,无法确定回调函数何时执行,以及是否在fire之前执行。遇到此类情况,可添加几个deviceone.print打印语句,以确认是on先执行还是fire先执行。

5. 有订阅就有取消订阅,取消订阅通过off函数实现。之所以较少使用,是因为在closePage时,系统会自动释放当前page作用域内订阅的所有消息。

然而,若消息订阅在app作用域,则需注意可能需要手动取消订阅。否则,触发消息时会导致函数执行多次的问题。


var page = sm("do_Page");
var count = ;
function f(){
deviceone.print("执行次数"+(count++));
}
page.on("message",f);
page.fire("message");
.page.off("message");
page.fire("message");

参考上述示例,打印结果仅执行一次,因为fire一次后即取消订阅。

作者最新文章