93

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

Master.prototype.__proto__ = EventEmitter.prototype;

(来源在这里

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

4

6 回答 6

91

正如该代码上面的注释所说,它将Master继承 from 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);

第二次更新:有了 ES6 类,建议EventEmitter现在扩展类:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

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

myEmitter.emit('event');

https://nodejs.org/api/events.html#events_events

于 2012-01-17T16:54:07.800 回答
87

ES6 风格类继承

Node 文档现在推荐使用类继承来制作你自己的事件发射器:

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');

注意:如果你在 中定义了一个constructor()函数MyEmitter,你应该super()从它调用以确保父类的构造函数也被调用,除非你有充分的理由不这样做。

于 2016-03-11T16:22:07.290 回答
40

要从另一个 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)设置原型,无论是否使用 using util.inherits,但不要EventEmitter为您的类的实例调用超级构造函数( ),它们将无法正确初始化。
  • 如果您调用超级构造函数但未设置原型,则 EventEmitter 方法将无法在您的对象上工作
  • 您可能会尝试使用超类 ( new EventEmitter)的初始化实例,Master.prototype而不是让子类构造函数Master调用超级构造函数EventEmitter;取决于超类构造函数的行为,它可能看起来在一段时间内可以正常工作,但不是一回事(并且不适用于 EventEmitter)。
  • 您可以尝试直接使用超级原型 ( Master.prototype = EventEmitter.prototype) 而不是通过 Object.create 添加额外的对象层;Master在有人猴子修补您的对象并且无意中也猴子修补EventEmitter及其所有其他后代之前,这似乎工作正常。每个“类”都应该有自己的原型。

同样:要从 EventEmitter(或实际上任何现有对象“类”)继承,您需要定义一个构造函数,该构造函数链接到超级构造函数并提供从超级原型派生的原型。

于 2014-05-28T17:55:53.377 回答
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);

理解 JavaScript OOP是我最近阅读的关于 ECMAScript 5 中 OOP 的最佳文章之一。

于 2012-01-17T16:55:42.870 回答
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 );

}

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

于 2013-03-01T16:50:50.580 回答
1

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

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part
于 2016-06-03T20:47:19.357 回答