Node.js - 继承自EventEmitter

时间:2012-01-17 16:44:46

标签: node.js eventemitter

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

Master.prototype.__proto__ = EventEmitter.prototype;

(来源here

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

6 个答案:

答案 0 :(得分:79)

正如代码上面的评论所说,它会使Master继承自EventEmitter.prototype,因此您可以使用该“类”的实例来发出和收听事件。

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

masterInstance = new Master();

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

// trigger the event
masterInstance.emit('an_event');

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

var EventEmitter = require('events').EventEmitter;
util.inherits(Master, EventEmitter);

答案 1 :(得分:69)

ES 6样式类继承

这些直接来自文档,但我认为很高兴将它们添加到这个热门问题中供所有人观看。

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {
  constructor() {
    super(); //must call super for "this" to be defined.
  }
}

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

我希望git thank添加任何人。 Event Emitter

注意:文档未在构造函数中调用super(),这将导致this未定义。请参阅此issue

答案 2 :(得分:38)

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

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

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

  • Javascript将对象行为分离为"构造函数"和"原型"。这些概念可以一起使用,但可以单独使用。
  • Javascript是一种非常具有可塑性的语言,人们使用它的方式不同,并没有单一的真正定义,即什么"继承"装置中。
  • 在很多情况下,你可以侥幸做出正确的一部分内容,你会发现大量的例子(包括对这个SO问题的一些其他答案)似乎工作得很好为你的情况。

对于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);

可能的缺点:

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

再次:要继承EventEmitter(或实际上任何现有对象" class"),你想要定义一个链接到超级构造函数的构造函数,并提供一个派生自超级原型的原型。 / p>

答案 3 :(得分:19)

这是在JavaScript中完成原型(prototypal?)继承的方式。 来自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);

Understanding JavaScript OOP是我最近在ECMAScript 5中阅读OOP的最佳文章之一。

答案 4 :(得分: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 );

}
道格拉斯·克罗克福德也有一些有趣的遗产模式: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

答案 5 :(得分:1)

添加到wprl的响应。他错过了“原型”部分:

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part