web-dev-qa-db-fra.com

Node.js - héritant de EventEmitter

Je vois ce modèle dans pas mal de bibliothèques Node.js:

Master.prototype.__proto__ = EventEmitter.prototype;

(source ici )

Est-ce que quelqu'un peut m'expliquer avec un exemple, pourquoi c'est un modèle si courant et quand c'est pratique?

83
jeffreyveon

Comme le commentaire ci-dessus, ce code indique que Master héritera de EventEmitter.prototype, vous pouvez donc utiliser des instances de cette "classe" pour émettre et écouter des événements.

Par exemple, vous pouvez maintenant faire:

masterInstance = new Master();

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

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

Mise à jour: comme de nombreux utilisateurs l'ont souligné, la méthode "standard" pour le faire dans Node serait d'utiliser "util.inherits":

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

Héritage de classe ES 6 Style

Celles-ci proviennent directement de la documentation, mais j’ai pensé qu’il serait bien de les ajouter à cette question populaire pour ceux qui cherchent.

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

J'aimerais bien git thank Qui l'a ajouté. émetteur d'événements .

Remarque: La documentation n'appelle pas super() dans le constructeur, ce qui entraînera la non-définition de this. Voir ceci issue .

75
Breedly

Pour hériter d'un autre objet Javascript, notamment de EventEmitter de Node.js mais vraiment de n'importe quel objet en général, vous devez faire deux choses:

  • fournissez un constructeur pour votre objet, qui l'initialise complètement; dans le cas où vous héritez d'un autre objet, vous souhaiterez probablement déléguer une partie de ce travail d'initialisation au super constructeur.
  • fournira un objet prototype qui sera utilisé comme [[proto]] pour les objets créés à partir de votre constructeur; dans le cas où vous héritez d'un autre objet, vous souhaiterez probablement utiliser une instance de l'autre objet comme prototype.

C’est plus compliqué en Javascript qu’il pourrait sembler en d’autres langues car

  • Javascript sépare le comportement des objets en "constructeur" et "prototype". Ces concepts sont destinés à être utilisés ensemble, mais peuvent être utilisés séparément.
  • Le javascript est une langue très malléable et les gens l'utilisent différemment et il n'y a pas de vraie définition de "héritage".
  • Dans de nombreux cas, vous pouvez vous contenter de faire un sous-ensemble de ce qui est correct et vous trouverez une multitude d'exemples à suivre (y compris quelques autres réponses à cette SO question) qui semblent bien fonctionner pour votre cas.

Pour le cas spécifique de EventEmitter de Node.js, voici ce qui fonctionne:

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

Défis possibles:

  • Si vous utilisez, définissez le prototype de votre sous-classe (Master.prototype), avec ou sans l'aide de util.inherits, mais n’appelez pas le super constructeur (EventEmitter) pour les instances de votre classe, elles ne seront pas correctement initialisées.
  • Si vous appelez le super constructeur mais ne définissez pas le prototype, les méthodes EventEmitter ne fonctionneront pas sur votre objet
  • Vous pouvez essayer d’utiliser une instance initialisée de la superclasse (new EventEmitter) comme Master.prototype au lieu d'avoir le constructeur de sous-classe Master appeler le super constructeur EventEmitter; En fonction du comportement du constructeur de la super-classe, cela peut sembler fonctionner pendant un certain temps, mais ce n'est pas la même chose (et cela ne fonctionnera pas pour EventEmitter).
  • Vous pouvez essayer d’utiliser le super prototype directement (Master.prototype = EventEmitter.prototype) au lieu d’ajouter une couche d’objet supplémentaire via Object.create; cela peut sembler fonctionner correctement jusqu'à ce que quelqu'un clippe votre objet Master et ait aussi par inadvertance monkeypatched EventEmitter et tous ses autres descendants. Chaque "classe" devrait avoir son propre prototype.

Encore une fois: pour hériter de EventEmitter (ou de tout objet "classe" existant), vous souhaitez définir un constructeur qui se lie au super constructeur et fournit un prototype dérivé du super prototype.

38
metamatt

C'est comment l'héritage prototypique (prototypal?) Se fait en JavaScript. De MDN :

Fait référence au prototype de l'objet, qui peut être un objet ou null (ce qui signifie généralement que l'objet est Object.prototype, qui n'a pas de prototype). Il est parfois utilisé pour implémenter une recherche de propriété basée sur un héritage prototype.

Cela fonctionne aussi bien:

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

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

Comprendre le langage OOP JavaScript est l’un des meilleurs articles que j’ai récemment lu sur OOP dans ECMAScript 5.

19
Daff

Je pensais que cette approche de http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm était plutôt soignée :

function EventedObject(){

  // Super constructor
  EventEmitter.call( this );

  return( this );

}

Douglas Crockford a également des modèles d’héritage intéressants: http://www.crockford.com/javascript/inheritance.html

Je trouve que l'héritage est moins souvent nécessaire dans JavaScript et Node.js. Mais en écrivant une application où l'héritage peut affecter l'évolutivité, je considérerais les performances mises en balance avec la maintenabilité. Sinon, je ne fonderais ma décision que sur les modèles qui conduiraient à de meilleures conceptions globales, qui seraient plus faciles à gérer et moins sujets aux erreurs.

Testez différents modèles dans jsPerf, en utilisant Google Chrome (V8) pour obtenir une comparaison approximative. V8 est le moteur JavaScript utilisé par Node.js et Chrome.

Voici quelques jsPerfs pour vous aider à démarrer:

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

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

http://jsperf.com/inheritance-perf

5
wprl

Pour ajouter à la réponse de wprl. Il a raté la partie "prototype":

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

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