使一个函数的行为像一个实例化的EventEmitter

我是作为JavaScript模块的主要API导出函数模式的狂热粉丝。 原因是在JS中,一个函数基本上可以做一个典型的对象可以做的事情,然后是一些事情。

所以这对我来说是典型的:

function stuff() {} function thing() { /* shortcut or default behavior */ } thing.stuff = stuff; module.exports = thing; 

现在我遇到了一个情况,我想让thing像EventEmitter的实例 一样 。 我希望它是一个构造函数。

为什么? 那么thing会真的沿着osPreferences的行,用一些选项调用它将数据保存到磁盘。 用户实例化它没有任何意义。 new OSPreferences()不会有太多用处,因为您的计算机一次只能遵循一组偏好。

但是,在我的API以外,任何时候都可能发生变化。 所以有一个巨大的好处:

 osPreferences.on('change', fn); 

所以问题是,什么是吸收EventEmitter 实例行为的可靠模式? 简单地遍历一次性实例的所有属性并将它们复制到目标函数是否足够好? 是否值得模仿inheritance的vs非inheritance的设置? 是否有任何奇怪的情况下考虑,因为默认情况下会改变? 还是有更好,更明智的方法?

简单地遍历一次性实例的所有属性并将它们复制到目标函数是否足够好?

是。 您甚至不需要创build一次性实例,只需将所有EventEmitter.prototype方法复制到您的函数中,然后将EventEmitter到它。

 function osPreferences() { … } for (var p in EventEmitter.prototype) osPreferences[p] = EventEmitter.prototype[p]; EventEmitter.call(osPreferences); 

是否值得模仿inheritance的vs非inheritance的设置?

不是真的。 你坚持使用你的api的单例,所以你根本不需要任何inheritance。

是否有任何奇怪的情况下考虑,因为默认情况下会改变?

不, EventEmitter编码相当防守 。 当然,你的听众可能会觉得不寻常…

循环并复制原始对象的方法和属性可能会失败,具体取决于EventEmitter的实现方式。 当大量使用closures来隐藏属性时,这种方法很可能会失败。

在我看来,最好的方法就是直接在你的函数实例上设置原型。 以你的榜样为例:

 var OSPreferences = function(){ // ... }; Object.setPrototypeOf(OSPreferences, new EventEmitter()); 

ES6类语法是最优雅的子类解决scheme。

 let EventEmitter = require('events'); //or `require('events').EventEmitter` in nodejs Class osPreferences extends EventEmitter{ constructor(){ super(); console.log(this); } } let osp = new osPreferences(); osp.on('change',function(){ console.log('osPreferences changed'); }); osp.emit('change'); 

不幸的是,最新的Node版本不支持类语法。 你必须改用iojs 。