Node.js ES6+环境下为JS类添加事件的最简方案及编辑器补全
类事件处理的通用实现与编辑器语法提示方案
问题描述
我一直在寻找类中处理事件的通用方法。具体来说,我正在开发一个Node.js模块,希望能为特定事件添加多个回调。代码示例如下:
class Example { constructor(...args) { // ... } doSomething(earg) { setTimeout(() => { // I want an event call here. // this.callEvent(eargs) or something like that, to pass // down arguments to callbacks. }, 3000); } on(event, callback, watch) { // ... } once(event, callback, watch) { // ... } } const example = new Example(); example.on('doSomethingEnd', (earg) => console.log(':P', earg));
请问在ES6及以上环境中,实现.on和.once方法最简洁且现代化的方式是什么?以及如何让Atom、VSCode等支持语法提示的编辑器实现自动补全?
一、.on和.once方法的简洁实现
方案1:直接继承Node.js内置EventEmitter
Node.js的events模块提供了成熟的EventEmitter类,已经封装了多回调管理、事件触发、一次性监听等逻辑,是最简洁的实现方式:
const { EventEmitter } = require('events'); class Example extends EventEmitter { constructor(...args) { super(...args); // 必须调用父类构造函数初始化事件系统 } doSomething(earg) { setTimeout(() => { // 使用emit触发事件,第一个参数为事件名,后续参数会传递给回调 this.emit('doSomethingEnd', earg); }, 3000); } } // 使用示例 const example = new Example(); example.on('doSomethingEnd', (earg) => console.log(':P', earg)); example.doSomething('test argument');
方案2:手动实现轻量版事件系统(无依赖)
如果是前端环境或不想依赖Node.js内置模块,可以手动实现轻量版逻辑:
class Example { constructor(...args) { // 用Map存储事件与回调列表,标记一次性回调 this._events = new Map(); } // 绑定持久事件回调 on(event, callback) { if (!this._events.has(event)) { this._events.set(event, []); } this._events.get(event).push({ callback, isOnce: false }); } // 绑定一次性事件回调 once(event, callback) { if (!this._events.has(event)) { this._events.set(event, []); } this._events.get(event).push({ callback, isOnce: true }); } // 触发事件 emit(event, ...args) { if (!this._events.has(event)) return; // 复制回调列表,避免触发过程中移除回调导致遍历异常 const callbacks = [...this._events.get(event)]; callbacks.forEach((item, index) => { item.callback(...args); // 执行后移除一次性回调 if (item.isOnce) { this._events.get(event).splice(index, 1); // 回调列表为空时清理事件 if (this._events.get(event).length === 0) { this._events.delete(event); } } }); } doSomething(earg) { setTimeout(() => { this.emit('doSomethingEnd', earg); }, 3000); } }
二、编辑器自动补全实现
要让VSCode、Atom等编辑器提供精准的语法提示和自动补全,需要添加类型声明,以下是两种常用方式:
方式1:在JS文件中添加JSDoc注释
直接在代码中通过JSDoc标注事件名称、参数类型:
/** * @typedef {'doSomethingEnd'} ExampleEventNames * @typedef {(earg: string) => void} ExampleCallback */ /** * 示例事件类 * @class */ class Example extends EventEmitter { constructor(...args) { super(...args); } /** * 执行操作并触发事件 * @param {string} earg - 传递给回调的参数 */ doSomething(earg) { setTimeout(() => { this.emit('doSomethingEnd', earg); }, 3000); } /** * 绑定事件监听 * @param {ExampleEventNames} event - 事件名称 * @param {ExampleCallback} callback - 事件回调 */ on(event, callback) { super.on(event, callback); } /** * 绑定一次性事件监听 * @param {ExampleEventNames} event - 事件名称 * @param {ExampleCallback} callback - 事件回调 */ once(event, callback) { super.once(event, callback); } }
方式2:编写单独的TypeScript类型声明文件
如果是发布的Node.js模块,建议创建example.d.ts类型声明文件:
import { EventEmitter } from 'events'; type ExampleEventNames = 'doSomethingEnd'; type ExampleCallback = (earg: string) => void; declare class Example extends EventEmitter { constructor(...args: any[]); doSomething(earg: string): void; on(event: ExampleEventNames, callback: ExampleCallback): this; once(event: ExampleEventNames, callback: ExampleCallback): this; } export = Example;
添加类型声明后,编辑器会自动识别事件名称、参数类型,提供精准的自动补全和语法校验。
内容的提问来源于stack exchange,提问作者bouncytorch
相关产品推荐
相关产品推荐

