Node.js - 继承自EventEmitter

jef*_*eon 84 node.js eventemitter

我在很多Node.js库中看到了这种模式:

Master.prototype.__proto__ = EventEmitter.prototype;
Run Code Online (Sandbox Code Playgroud)

(来源这里)

有人可以用一个例子向我解释,为什么这是一个如此常见的模式,什么时候它很方便?

ale*_*lex 80

正如代码上面的注释所说,它将Master继承EventEmitter.prototype,因此您可以使用该"类"的实例来发出和侦听事件.

例如,你现在可以这样做:

masterInstance = new Master();

masterInstance.on('an_event', function () {
  console.log('an event has happened');
});

// trigger the event
masterInstance.emit('an_event');
Run Code Online (Sandbox Code Playgroud)

更新:正如许多用户指出的那样,在Node中执行此操作的"标准"方法是使用'util.inherits':

var EventEmitter = require('events').EventEmitter;
util.inherits(Master, EventEmitter);
Run Code Online (Sandbox Code Playgroud)

  • (只是提醒一下`require('events').EventEmitter`首先 - 我总是忘记.这是文档的链接,万一其他人需要它:http://nodejs.org/api/events.html #events_class_events_eventemitter) (3认同)
  • BTW,实例的约定是小写第一个字母,因此`MasterInstance`应该是`masterInstance`. (3认同)
  • `util.inherits`做了一个令人讨厌的事情,将`super_`属性注入`Master`对象.这是不必要的,并试图像经典继承一样对待原型继承.请查看[this](http://www.crockford.com/javascript/inheritance.html)页面的底部以获取解释. (3认同)
  • @loretoparisi只是`Master.prototype = EventEmitter.prototype;`.不需要超级.你也可以使用ES6扩展(在`util.inherits`中的Node.js文档中鼓励它),比如这个`类扩展EventEmitter` - 你得到经典的`super()`,但是没有注入任何东西到`Master`. (2认同)

Bre*_*dly 77

ES 6样式类继承

这些直接来自文档,但我认为将它们添加到这个流行的问题对任何人看都很好.

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {
  // Add any custom methods here
}

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
  console.log('an event occurred!');
});
myEmitter.emit('event');
Run Code Online (Sandbox Code Playgroud)

我想constructor()添加任何人.事件发射器.

注意:文档不会MyEmitter在构造函数中调用,这将导致super()未定义.看到这个问题.

  • 这些评论不正确,在这种情况下最终会产生误导.调用`super()`不是****,只要你不需要/定义构造函数,因此Breedly的原始答案(参见编辑历史)是完全正确的.在这种情况下,您可以在repl中复制并粘贴此相同的示例,完全删除构造函数,它将以相同的方式工作.这是完全有效的语法. (9认同)

met*_*att 39

要继承另一个Javascript对象,特别是Node.js的EventEmitter,但实际上通常是任何对象,你需要做两件事:

  • 为你的对象提供一个构造函数,它完全初始化了对象; 在您继承其他对象的情况下,您可能希望将一些初始化工作委托给超级构造函数.
  • 提供一个原型对象,用作[[proto]]从构造函数创建的对象; 如果您从其他对象继承,您可能希望使用另一个对象的实例作为原型.

这在Javascript中比在其他语言中看起来更复杂,因为

  • Javascript将对象行为分为"构造函数"和"原型".这些概念可以一起使用,但可以单独使用.
  • Javascript是一种非常具有可塑性的语言,人们使用它的方式不同,并没有单一的"继承"含义的真正定义.
  • 在许多情况下,你可以放弃做一些正确的子集,你会发现大量的例子(包括对这个问题的一些其他答案)似乎对你的情况很好.

对于Node.js的EventEmitter的具体情况,这是有用的:

var EventEmitter = require('events').EventEmitter;
var util = require('util');

// Define the constructor for your derived "class"
function Master(arg1, arg2) {
   // call the super constructor to initialize `this`
   EventEmitter.call(this);
   // your own initialization of `this` follows here
};

// Declare that your class should use EventEmitter as its prototype.
// This is roughly equivalent to: Master.prototype = Object.create(EventEmitter.prototype)
util.inherits(Master, EventEmitter);
Run Code Online (Sandbox Code Playgroud)

可能的缺点:

  • 如果您使用为您的子类(Master.prototype)设置原型,使用或不使用util.inherits,但不要EventEmitter为您的类的实例调用超级构造函数(),它们将无法正确初始化.
  • 如果调用超级构造函数但未设置原型,则EventEmitter方法将不适用于您的对象
  • 您可能尝试使用超类(new EventEmitter)的初始化实例,Master.prototype而不是让子类构造函数Master调用超级构造函数EventEmitter; 取决于超类构造函数的行为,可能看起来它在一段时间内工作正常,但不是同一件事(并且不适用于EventEmitter).
  • 您可以尝试直接使用super原型(Master.prototype = EventEmitter.prototype),而不是通过Object.create添加额外的对象层; 这可能看起来像是工作正常,直到有人monkeypatches你的对象Master,并无意中也monkeypatched EventEmitter和所有其他后代.每个"类"都应该有自己的原型.

再次:要继承EventEmitter(或实际上任何现有对象"class"),您需要定义一个构造函数,该构造函数链接到超级构造函数并提供从超级原型派生的原型.


Daf*_*aff 19

这就是在JavaScript中完成原型(原型?)继承的方式.来自MDN:

引用对象的原型,可以是对象或null(通常意味着对象是Object.prototype,它没有原型).它有时用于实现基于原型继承的属性查找.

这也有效:

var Emitter = function(obj) {
    this.obj = obj;
}

// DON'T Emitter.prototype = new require('events').EventEmitter();
Emitter.prototype = Object.create(require('events').EventEmitter.prototype);
Run Code Online (Sandbox Code Playgroud)

理解JavaScript OOP是我最近在ECMAScript 5中阅读OOP的最佳文章之一.

  • `Y.prototype = new X();`是一个反模式,请使用`Y.prototype = Object.create(X.prototype);` (7认同)
  • `new X()`实例化一个`X.prototype`的实例,并通过调用它来初始化它.`Object.create(X.prototype)`只是实例化一个实例.你没有得到`Emitter.prototype`来初始化.我找不到一篇解释这个问题的好文章. (4认同)

wpr*_*prl 5

我认为这种方法来自http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm非常简洁:

function EventedObject(){

  // Super constructor
  EventEmitter.call( this );

  return( this );

}
Run Code Online (Sandbox Code Playgroud)

Douglas Crockford也有一些有趣的继承模式:http://www.crockford.com/javascript/inheritance.html

我发现在JavaScript和Node.js中经常不需要继承.但在编写一个继承可能会影响可伸缩性的应用程序时,我会考虑性能与可维护性的权衡.否则,我只会决定哪些模式可以带来更好的整体设计,更易于维护,更不容易出错.

在jsPerf中测试不同的模式,使用Google Chrome(V8)进行粗略比较.V8是Node.js和Chrome使用的JavaScript引擎.

这里有一些jsPerfs可以帮助你入门:

http://jsperf.com/prototypes-vs-functions/4

http://jsperf.com/inheritance-proto-vs-object-create

http://jsperf.com/inheritance-perf