JavaScript 自定义事件:原理、应用场景与模块化协作

作者:像素猫时间:2026-10-22 09:47:57浏览:254

本文深入解析 JavaScript 自定义事件的功能与用法。通过对比传统同步调用与基于 Event 的异步通信,阐述如何利用 document 作为公共事件总线实现模块间解耦。文章涵盖 Event 与 CustomEvent 的区别,提供游戏加载、多对象通知等完整代码示例,展示如何在模块化架构中高效协作。

JavaScript 自定义事件概述

本文通过具体实例详细解析 JavaScript 中自定义事件的功能及其实际用法,旨在为开发者提供清晰的参考指南。

概述

自定义事件是否真的难以落地?

为什么需要自定义事件

过去,由于 JavaScript 缺乏模块化开发规范且协作场景较少,自定义事件往往难以发挥价值。事件的本质是通信机制,即消息传递,只有在存在多个对象或多个模块交互的场景下,通过事件进行通信才显得必要。随着现代前端开发中模块化趋势的普及,利用自定义事件实现模块间的协作已成为可能且高效的手段。

自定义事件的应用场景

自定义事件的应用场景有哪些?

既然事件的本质是消息,而事件模式本质上是观察者模式的一种实现,那么任何适用观察者模式的场景,自然也可以应用事件模式。具体而言,以下两种情况非常适合使用自定义事件:

1、当一个目标对象的状态发生改变,需要触发多个观察者进行相应调整时。

例如:当元素 A 被点击后,需要让元素 B 显示鼠标坐标,元素 C 显示提示信息,元素 D 执行其他操作等。

2、在分模块协作中,为了实现模块间的解耦。

例如:模块 A 由开发者甲负责,模块 B 由开发者乙负责,且模块 B 必须在模块 A 运行结束后才能启动。

传统的写法通常将相关逻辑直接写在一个方法内部:


function doSomething(){
  A();
  B();
}

这种写法每次增加新功能时都需要修改 A 的点击处理函数,导致代码耦合度高,难以扩展。

使用自定义事件的写法如下:


//1、创建事件
var clickElem = new Event("clickElem");
//2、注册事件监听器
elem.addEventListener("clickElem",function(e){
  //干点事
})
//3、触发事件
elem.dispatchEvent(clickElem);

可以看到,通过 elem 调用 dispatchEvent 方法触发的事件,只有注册在 elem 上的监听器才能接收到。这种方式显得局限,因为自己触发事件通知自己去执行操作,缺乏实际意义。

关于创建自定义事件的详细规范,可参考 MDN 文档:Creating_and_triggering_events

自定义事件的实战应用

实际应用案例

从前文的描述可知,元素 A 通过 dispatchEvent 方法触发的事件,仅能被 A 上注册的监听器捕获。

我们期望的效果是:当其他对象执行了某项操作后,发送一条消息通知我们,以便我们做出相应的状态更新。实现这一目标的方法是:在一个公共对象(如 document)上统一监听和触发事件,这种方式具有极高的实用价值。

案例一:通知多个对象

要实现元素 A 点击后,元素 B 显示鼠标位置,元素 C 显示提示的功能,代码实现如下:

文件:a.js


import b from "./b"
import c from "./c"
var a = document.getElementById("a");
a.addEventListener("click",function(e){
  var clickA = new Event("clickA");
  document.dispatchEvent(clickA);
});

注意:即使 import 导入的变量在后续代码中未直接使用,也必须保留 import 语句,否则会导致模块加载错误。

文件 b.js:


var b = document.getElementById("b");
document.addEventListener("clickA",function(e){
  b.innerHTML = "(128,345)";
})

文件 c.js:


var c = document.getElementById("c");
document.addEventListener("clickA",function(e){
  c.innerHTML = "你点了A";
})

采用这种写法,三个模块之间完全无需关心彼此的存在,也不知道对方具体是谁,耦合度极低。各模块可以独立编写且互不影响,这实际上就是观察者模式的具体实现。

案例二:游戏框架开发

假设正在开发一个游戏,流程包括启动游戏、加载图片和音乐,加载完成后渲染场景和音效。加载资源与渲染场景由不同的开发人员负责,实现方式如下:

文件:index.js


import loadImage from "./loadImage"
import loadMusic from "./loadMusic"
import initScene from "./initScene"  
var start = document.getElementById("start");
start.addEventListener("click",function(e){
  console.log("游戏开始!");
  document.dispatchEvent(new Event("gameStart"));
})

文件:loadImage.js


// 加载图片
document.addEventListener("gameStart",function(){
  console.log("加载图片...");
  setTimeout(function(){
    console.log("加载图片完成");
    document.dispatchEvent(new Event("loadImageSuccess"));
  },1000);
});

文件:loadMusic.js


//加载音乐
document.addEventListener("gameStart",function(){
  console.log("加载音乐...");
  setTimeout(function(){
    console.log("加载音乐完成");
    document.dispatchEvent(new Event("loadMusicSuccess"));
  },2000);
});

文件:initScene.js


//渲染场景
document.addEventListener("loadImageSuccess",function(e){
  console.log("使用图片创建场景...");
  setTimeout(function(){
    console.log("创建场景完成");
  },2000)
});
//渲染音效
document.addEventListener("loadMusicSuccess",function(e){
  console.log("使用音乐创建音效...");
  setTimeout(function(){
    console.log("创建音效完成");
  },500)
});

加载模块与渲染模块相互独立,互不干扰,且系统易于扩展。

事件携带自定义信息

携带自定义信息

此外,事件还可以用于传递自定义数据:


var event = new CustomEvent('myEvent', { 'dataName': dataContent });
document.dispatchEvent(event);

(注意:传递自定义信息时必须使用 CustomEvent 构造函数,而不能使用普通的 Event。)

随后在监听函数中获取传递的数据:


document.addEventListener("myEvent",function(e){
  console.log(e.dataName);
})

这一功能非常实用,常用于模块间的数据交换。

附:点击此处查看 github 示例

PS:这里再为大家附上 javascript 系统自带事件参考表供大家参考查询:

javascript 事件与功能说明大全: http://tools.jb51.net/table/javascript_event

更多关于 JavaScript 相关内容感兴趣的读者可查看本站专题:《JavaScript 事件相关操作与技巧大全》、《JavaScript 操作 DOM 技巧总结》、《JavaScript 错误与调试技巧总结》、《JavaScript 数据结构与算法技巧总结》、《JavaScript 遍历算法与技巧总结》及《JavaScript 数学运算用法总结》

希望本文所述对大家 JavaScript 程序设计有所帮助。

作者最新文章