Node.js - Erben von EventEmitter

89

Ich sehe dieses Muster in einigen Node.js-Bibliotheken:

Master.prototype.__proto__ = EventEmitter.prototype;

(Quelle hier )

Kann mir bitte jemand anhand eines Beispiels erklären, warum dies so häufig vorkommt und wann es praktisch ist?

Jeffreyveon
quelle
Siehe diese Frage für Informationen stackoverflow.com/questions/5398487/…
Juicy Scripter
14
Hinweis __proto__ist ein Anti-Muster, bitte verwenden SieMaster.prototype = Object.create(EventEmitter.prototype);
Raynos
69
Verwenden util.inherits(Master, EventEmitter);
Sie
1
@ Raynos Was ist ein Anti-Pattern?
Starbeamrainbowlabs
1
Dies ist jetzt mit Konstruktoren der ES6-Klasse einfacher. Überprüfen Sie die Kompatibilität hier: kangax.github.io/compat-table/es6 . Überprüfen Sie die Dokumente oder meine Antwort unten.
Breedly

Antworten:

84

Wie der Kommentar über diesem Code besagt, wird er von Mastererben EventEmitter.prototype, sodass Sie Instanzen dieser 'Klasse' verwenden können, um Ereignisse auszugeben und abzuhören.

Zum Beispiel könnten Sie jetzt tun:

masterInstance = new Master();

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

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

Update : Wie viele Benutzer betonten, besteht die "Standard" -Methode in Node darin, "util.inherits" zu verwenden:

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

2. Update : Mit ES6-Klassen wird empfohlen, die EventEmitterKlasse jetzt zu erweitern:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

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

myEmitter.emit('event');

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

Alessioalex
quelle
3
(nur eine kleine Erinnerung, um es require('events').EventEmitterzuerst zu tun - ich vergesse es immer. Hier ist der Link zu den Dokumenten, falls jemand anderes es braucht: nodejs.org/api/events.html#events_class_events_eventemitter )
mikermcneil
3
BTW, ist die Konvention für Instanzen den ersten Buchstaben in Kleinbuchstaben, so MasterInstancesein sollte masterInstance.
Khoomeister
Und wie können Sie überprüfen, ob: masterInstance-Instanz von Master?
Jayarjo
3
util.inheritsmacht eine böse Sache, die super_Eigenschaft in das MasterObjekt zu injizieren . Es ist unnötig und versucht, prototypische Vererbung wie klassische Vererbung zu behandeln. Weitere Informationen finden Sie am Ende dieser Seite.
klh
2
@loretoparisi nur Master.prototype = EventEmitter.prototype;. Keine Notwendigkeit für Supers. Sie können auch ES6-Erweiterungen (und dies wird in Node.js Dokumenten empfohlen util.inherits) wie folgt verwenden class Master extends EventEmitter- Sie werden klassisch super(), ohne jedoch etwas zu injizieren Master.
klh
80

Vererbung der ES6-Stilklasse

In den Node-Dokumenten wird jetzt empfohlen, die Klassenvererbung zu verwenden, um Ihren eigenen Ereignisemitter zu erstellen:

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

Hinweis: Wenn Sie eine constructor()Funktion in definieren MyEmitter, sollten Sie sie aufrufen super(), um sicherzustellen, dass auch der Konstruktor der übergeordneten Klasse aufgerufen wird, es sei denn, Sie haben einen guten Grund, dies nicht zu tun.

Rassig
quelle
9
Diese Kommentare sind nicht korrekt und in diesem Fall irreführend. Ein Aufruf super()ist nicht erforderlich , solange Sie keinen Konstruktor benötigen / definieren. Daher war Breedlys ursprüngliche Antwort (siehe EDIT-Verlauf) völlig korrekt. In diesem Fall können Sie dasselbe Beispiel kopieren und in die Repl einfügen, den Konstruktor vollständig entfernen und es funktioniert auf die gleiche Weise. Das ist eine vollkommen gültige Syntax.
Nobita
39

Um von einem anderen Javascript-Objekt zu erben, insbesondere von EventEmitter von Node.js, aber wirklich von jedem Objekt im Allgemeinen, müssen Sie zwei Dinge tun:

  • Stellen Sie einen Konstruktor für Ihr Objekt bereit, der das Objekt vollständig initialisiert. Für den Fall, dass Sie von einem anderen Objekt erben, möchten Sie wahrscheinlich einen Teil dieser Initialisierungsarbeit an den Superkonstruktor delegieren.
  • Stellen Sie ein Prototypobjekt bereit, das als [[proto]]für Objekte verwendet wird, die von Ihrem Konstruktor erstellt wurden. Für den Fall, dass Sie von einem anderen Objekt erben, möchten Sie wahrscheinlich eine Instanz des anderen Objekts als Prototyp verwenden.

Dies ist in Javascript komplizierter als es in anderen Sprachen erscheinen mag, weil

  • Javascript unterteilt das Objektverhalten in "Konstruktor" und "Prototyp". Diese Konzepte sollen zusammen verwendet werden, können aber separat verwendet werden.
  • Javascript ist eine sehr formbare Sprache und wird von Menschen unterschiedlich verwendet. Es gibt keine einzige wahre Definition dessen, was "Vererbung" bedeutet.
  • In vielen Fällen können Sie mit einer Teilmenge der Richtigkeit davonkommen, und Sie werden unzählige Beispiele finden (einschließlich einiger anderer Antworten auf diese SO-Frage), die für Ihren Fall gut zu funktionieren scheinen.

Für den speziellen Fall von Node.js EventEmitter funktioniert Folgendes:

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

Mögliche Schwächen:

  • Wenn Sie den Prototyp für Ihre Unterklasse (Master.prototype) mit oder ohne Verwendung festlegen util.inherits, aber den Superkonstruktor ( EventEmitter) für Instanzen Ihrer Klasse nicht aufrufen , werden sie nicht ordnungsgemäß initialisiert.
  • Wenn Sie den Superkonstruktor aufrufen, aber den Prototyp nicht festlegen, funktionieren die EventEmitter-Methoden nicht für Ihr Objekt
  • Sie könnten versuchen, eine initialisierte Instanz der Superklasse ( new EventEmitter) zu verwenden, Master.prototypeanstatt dass der Unterklassenkonstruktor Masterden Superkonstruktor aufruft EventEmitter. Abhängig vom Verhalten des Superklassenkonstruktors scheint dies für eine Weile gut zu funktionieren, ist aber nicht dasselbe (und funktioniert nicht für EventEmitter).
  • Sie können versuchen, den Super-Prototyp direkt ( Master.prototype = EventEmitter.prototype) zu verwenden, anstatt über Object.create eine zusätzliche Objektebene hinzuzufügen. Dies scheint gut zu funktionieren, bis jemand Ihr Objekt Mastermonkeypatched EventEmitterund versehentlich auch monkeypatched und alle seine anderen Nachkommen hat. Jede "Klasse" sollte einen eigenen Prototyp haben.

Nochmals: Um von EventEmitter (oder einer wirklich vorhandenen Objektklasse) zu erben, möchten Sie einen Konstruktor definieren, der mit dem Superkonstruktor verkettet ist und einen Prototyp bereitstellt, der vom Superprototyp abgeleitet ist.

Metamatt
quelle
19

So erfolgt die prototypische (prototypische?) Vererbung in JavaScript. Von MDN :

Bezieht sich auf den Prototyp des Objekts, der ein Objekt oder null sein kann (was normalerweise bedeutet, dass das Objekt Object.prototype ist, das keinen Prototyp hat). Es wird manchmal verwendet, um die Suche nach prototypvererbungsbasierten Eigenschaften zu implementieren.

Das funktioniert auch:

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

// DON'T Emitter.prototype = new require('events').EventEmitter();
Emitter.prototype = Object.create(require('events').EventEmitter.prototype);

Das Verständnis von JavaScript OOP ist einer der besten Artikel, die ich in letzter Zeit über OOP in ECMAScript 5 gelesen habe.

Daff
quelle
7
Y.prototype = new X();ist ein Anti-Muster, bitte verwenden SieY.prototype = Object.create(X.prototype);
Raynos
Das ist gut zu wissen. Kann ich irgendwo mehr nachlesen? Würde mich interessieren, wie sich die resultierenden Objekte unterscheiden.
Daff
4
new X()Instanziieren Sie eine Instanz von X.prototypeund initialisieren Sie sie, indem Sie sie aufrufen X. Object.create(X.prototype)instanziiert nur eine Instanz. Sie müssen nicht Emitter.prototypeinitialisiert werden. Ich kann keinen guten Artikel finden, der dies erklärt.
Raynos
Das macht Sinn. Vielen Dank für den Hinweis. Ich versuche immer noch, auf Node gute Gewohnheiten zu entwickeln. Die Browser sind einfach nicht für ECMA5 da (und wie ich verstanden habe, ist die Unterlegscheibe nicht die zuverlässigste).
Daff
1
Der andere Link ist ebenfalls defekt. Versuchen Sie dieses: robotlolita.github.io/2011/10/09/…
jlukanta
5

Ich fand diesen Ansatz von http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm ziemlich ordentlich:

function EventedObject(){

  // Super constructor
  EventEmitter.call( this );

  return( this );

}

Douglas Crockford hat auch einige interessante Vererbungsmuster: http://www.crockford.com/javascript/inheritance.html

Ich finde, dass Vererbung in JavaScript und Node.js seltener benötigt wird. Beim Schreiben einer App, bei der die Vererbung die Skalierbarkeit beeinträchtigen könnte, würde ich jedoch die Leistung als gegen die Wartbarkeit abgewogen betrachten. Andernfalls würde ich meine Entscheidung nur darauf stützen, welche Muster zu besseren Gesamtdesigns führen, wartbarer und weniger fehleranfällig sind.

Testen Sie verschiedene Muster in jsPerf mit Google Chrome (V8), um einen groben Vergleich zu erhalten. V8 ist die JavaScript-Engine, die sowohl von Node.js als auch von Chrome verwendet wird.

Hier sind einige jsPerfs, um Ihnen den Einstieg zu erleichtern:

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

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

http://jsperf.com/inheritance-perf

wprl
quelle
1
Ich habe diesen Ansatz und beides ausprobiert emitund onkomme als undefiniert heraus.
Dopatraman
Ist nicht die Rückkehr (dies); nur zum verketten?
Blablabla
1

Zur Antwort von wprl hinzufügen. Er vermisste den "Prototyp" Teil:

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part
guatedude2
quelle
1
Eigentlich sollten Sie Object.create anstelle von new verwenden, da Sie sonst den Instanzstatus sowie das Verhalten des Prototyps erhalten, wie an anderer Stelle erläutert . Aber besser ES6 und Transpile verwenden oder util.inheritsda viele kluge Leute diese Optionen für Sie auf dem neuesten Stand halten.
Cool Blue