【问题标题】:Node.js - inheriting from EventEmitterNode.js - 从 EventEmitter 继承
【发布时间】:2012-02-12 11:30:43
【问题描述】:

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

Master.prototype.__proto__ = EventEmitter.prototype;

(来源here

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

【问题讨论】:

  • 请参阅此问题以获取信息stackoverflow.com/questions/5398487/…
  • 注意__proto__是反模式,请使用Master.prototype = Object.create(EventEmitter.prototype);
  • 其实用util.inherits(Master, EventEmitter);
  • @Raynos 什么是反模式?
  • 现在使用 ES6 类构造函数更容易。在此处检查兼容性:kangax.github.io/compat-table/es6。检查下面的文档或我的答案。

标签: node.js eventemitter


【解决方案1】:

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

第二次更新:有了 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

【讨论】:

  • (只是提醒您先做require('events').EventEmitter -- 我总是忘记。这是文档的链接以防其他人需要它:nodejs.org/api/events.html#events_class_events_eventemitter
  • 顺便说一句,实例的约定是第一个字母小写,所以MasterInstance应该是masterInstance
  • 你如何保留检查是否:masterInstance instanceof Master的能力?
  • util.inheritssuper_ 属性注入Master 对象是一件令人讨厌的事情。这是不必要的,并试图将原型继承视为经典继承。请看this页面底部的解释。
  • @loretoparisi 只是Master.prototype = EventEmitter.prototype;。不需要超人。您还可以像 class Master extends EventEmitter 一样使用 ES6 扩展(并且在 util.inherits 上的 Node.js 文档中鼓励使用) - 您会得到经典的 super(),但无需向 Master 注入任何内容。
【解决方案2】:

ES6 样式类继承

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

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

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

【讨论】:

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

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

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

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

【讨论】:

  • 实际上,您应该使用 Object.create 而不是 new 否则您将获得实例状态以及原型上的行为,如 elsewhere 所述。但最好使用 ES6 和 transpile 或 util.inherits,因为很多聪明人会为你更新这些选项。
【解决方案4】:

这就是在 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);

Understanding JavaScript OOP 是我最近读到的关于 ECMAScript 5 中 OOP 的最佳文章之一。

【讨论】:

  • Y.prototype = new X(); 是反模式,请使用Y.prototype = Object.create(X.prototype);
  • 很高兴知道这一点。我可以在某处阅读更多内容吗?会对生成的对象有何不同感兴趣。
  • new X() 实例化X.prototype 的实例并通过调用X 对其进行初始化。 Object.create(X.prototype) 只是实例化一个实例。你不需要 Emitter.prototype 来初始化。我找不到解释这一点的好文章。
  • 这是有道理的。感谢您指出。仍在尝试在 Node.js 上养成良好的习惯。 ECMA5 的浏览器不存在(据我所知,shim 不是最可靠的)。
  • 另一个链接也坏了。试试这个:robotlolita.github.io/2011/10/09/…
【解决方案5】:

要继承另一个 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 添加额外的对象层;这似乎工作正常,直到有人猴子修补您的对象Master 并且无意中也猴子修补了EventEmitter 及其所有其他后代。每个“类”都应该有自己的原型。

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

【讨论】:

    【解决方案6】:

    我认为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

    【讨论】:

    • 我已经尝试过这种方法,emiton 都显示为未定义。
    • 不是回报(这个);只是为了链接?
    猜你喜欢
    • 1970-01-01
    • 2016-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    • 2011-07-17
    • 2012-12-17
    相关资源
    最近更新 更多