javascript原型模式不直接实现事件驱动架构,但通过将on/emit/off等方法挂载在eventemitter.prototype上,使子类继承后自动获得可复用、低内存占用的事件能力,并支持跨环境扩展与定制。

JavaScript 原型模式本身不直接实现事件驱动架构,但它为构建可复用、可继承的事件系统提供了天然支持。事件驱动架构的核心在于解耦“谁触发”和“谁响应”,而原型链能帮助你把事件注册、分发、移除等通用逻辑封装在基类(如 EventEmitter)中,再让具体对象通过原型继承获得这些能力,无需重复编写。
原型链如何支撑事件系统复用
你可以定义一个基础构造函数,其原型上挂载 on、emit、off 等方法。所有继承它的实例自动具备事件能力,且共享同一套逻辑,节省内存,也便于统一维护。
- 构造函数只负责初始化私有状态(比如
this._events = {}),不重复实现业务逻辑 - 所有事件操作都定义在
EventEmitter.prototype上,子类原型链自然继承 - 子类可通过
Object.setPrototypeOf(Child.prototype, EventEmitter.prototype)或 ES6class语法继承,保持原型关系清晰
基于原型的简易 EventEmitter 实现
以下是一个轻量、可继承的原型式事件发射器:
function EventEmitter() {
this._events = Object.create(null);
}
EventEmitter.prototype.on = function(type, listener) {
if (!this._events[type]) this._events[type] = [];
this._events[type].push(listener);
return this;
};
EventEmitter.prototype.emit = function(type, data) {
const handlers = this._events[type];
if (handlers && handlers.length) {
handlers.forEach(fn => fn.call(this, data));
}
return this;
};
EventEmitter.prototype.off = function(type, listener) {
const handlers = this._events[type];
if (handlers) {
const i = handlers.indexOf(listener);
if (i > -1) handlers.splice(i, 1);
}
return this;
};
使用时只需让业务对象继承它:
function Button(name) {
EventEmitter.call(this);
this.name = name;
}
Object.setPrototypeOf(Button.prototype, EventEmitter.prototype);
const btn = new Button('submit');
btn.on('click', () => console.log('按钮被点了'));
btn.emit('click');
与现代框架中事件机制的差异点
浏览器原生 EventTarget(如 HTMLElement)本身就是基于原型设计的:所有 DOM 元素都继承自 EventTarget.prototype,因此自带 addEventListener 和 dispatchEvent。但它的事件系统是硬编码在引擎中的,不可修改;而你用原型手写的 EventEmitter 是完全可控的,适合:
- 跨环境复用(浏览器 + Node.js)
- 添加拦截逻辑(如事件节流、日志埋点)
- 支持通配符、命名空间等扩展语义
- 与状态管理(如观察者模式)深度集成
注意事项:避免原型污染与内存泄漏
手动实现时需注意两个常见陷阱:
- 不要在原型上直接写可变对象(如
EventEmitter.prototype._events = {}),否则所有实例共享同一个对象——必须在构造函数内初始化 - 绑定的回调若含闭包或引用了大对象,且未调用
off,容易造成内存泄漏;建议在销毁实例时统一清理(可加destroy方法) - 若需支持异步事件分发(如 Promise 化 emit),应在原型方法中明确处理微任务队列,不能依赖同步执行顺序
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











