Veo este patrón en bastantes bibliotecas de Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(fuente aquí )
¿Alguien puede explicarme con un ejemplo por qué es un patrón tan común y cuándo es útil?
node.js
eventemitter
Jeffreyveon
fuente
fuente
__proto__
es un anti-patrón, por favor useMaster.prototype = Object.create(EventEmitter.prototype);
util.inherits(Master, EventEmitter);
Respuestas:
Como dice el comentario anterior de ese código, hará
Master
heredaráEventEmitter.prototype
, por lo que puede usar instancias de esa 'clase' para emitir y escuchar eventos.Por ejemplo, ahora podría hacer:
masterInstance = new Master(); masterInstance.on('an_event', function () { console.log('an event has happened'); }); // trigger the event masterInstance.emit('an_event');
Actualización : como señalaron muchos usuarios, la forma 'estándar' de hacerlo en Node sería usar 'util.inherits':
var EventEmitter = require('events').EventEmitter; util.inherits(Master, EventEmitter);
2da actualización : con las clases de ES6 sobre nosotros, se recomienda extender la
EventEmitter
clase ahora:const EventEmitter = require('events'); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on('event', () => { console.log('an event occurred!'); }); myEmitter.emit('event');
Ver https://nodejs.org/api/events.html#events_events
fuente
require('events').EventEmitter
primero, siempre lo olvido. Aquí está el enlace a los documentos en caso de que alguien más lo necesite: nodejs.org/api/events.html#events_class_events_eventemitter )MasterInstance
debería sermasterInstance
.util.inherits
hace algo desagradable al inyectar lasuper_
propiedad en elMaster
objeto. Es innecesario e intenta tratar la herencia prototípica como la herencia clásica. Eche un vistazo al final de esta página para obtener una explicación.Master.prototype = EventEmitter.prototype;
. No se necesitan alzas. También puede usar extensiones de ES6 (y se recomienda en los documentos de Node.jsutil.inherits
) de esta maneraclass Master extends EventEmitter
: obtiene clásicosuper()
, pero sin inyectar nada enMaster
.Herencia de clase de estilo ES6
Los documentos de Node ahora recomiendan usar la herencia de clases para crear su propio emisor de eventos:
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');
Nota: Si define una
constructor()
función enMyEmitter
, debe llamarsuper()
desde ella para asegurarse de que también se llame al constructor de la clase principal, a menos que tenga una buena razón para no hacerlo.fuente
super()
se requiere llamar , siempre que no necesite / defina un constructor, por lo tanto, la respuesta original de Breedly (ver EDITAR historial) fue completamente correcta. En este caso, puede copiar y pegar este mismo ejemplo en la respuesta, eliminar el constructor por completo y funcionará de la misma manera. Esa es una sintaxis perfectamente válida.Para heredar de otro objeto Javascript, EventEmitter de Node.js en particular, pero realmente cualquier objeto en general, necesita hacer dos cosas:
[[proto]]
objeto creado a partir de su constructor; en el caso de que esté heredando de algún otro objeto, probablemente quiera usar una instancia del otro objeto como su prototipo.Esto es más complicado en Javascript de lo que podría parecer en otros idiomas porque
Para el caso específico de EventEmitter de Node.js, esto es lo que funciona:
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);
Posibles debilidades:
util.inherits
, pero no llame al superconstructor (EventEmitter
) para instancias de su clase, no se inicializarán correctamente.new EventEmitter
) enMaster.prototype
lugar de que el constructor de la subclaseMaster
llame al superconstructorEventEmitter
; dependiendo del comportamiento del constructor de la superclase, puede parecer que funciona bien por un tiempo, pero no es lo mismo (y no funcionará para EventEmitter).Master.prototype = EventEmitter.prototype
) en lugar de agregar una capa adicional de objeto a través de Object.create; esto puede parecer que está funcionando bien hasta que alguien parchea tu objetoMaster
y, sin darse cuenta, también parcheaEventEmitter
y todos sus otros descendientes. Cada "clase" debe tener su propio prototipo.Nuevamente: para heredar de EventEmitter (o realmente cualquier "clase" de objeto existente), desea definir un constructor que se encadena al superconstructor y proporciona un prototipo que se deriva del superprototipo.
fuente
Así es como se realiza la herencia prototípica (¿prototípica?) En JavaScript. De MDN :
Esto también funciona:
var Emitter = function(obj) { this.obj = obj; } // DON'T Emitter.prototype = new require('events').EventEmitter(); Emitter.prototype = Object.create(require('events').EventEmitter.prototype);
Comprender JavaScript OOP es uno de los mejores artículos que leí últimamente sobre OOP en ECMAScript 5.
fuente
Y.prototype = new X();
es un anti-patrón, por favor useY.prototype = Object.create(X.prototype);
new X()
instancia una instancia deX.prototype
y la inicializa invocándolaX
.Object.create(X.prototype)
simplemente crea una instancia. NoEmitter.prototype
desea inicializarse. No puedo encontrar un buen artículo que explique esto.Pensé que este enfoque de http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm era bastante bueno:
function EventedObject(){ // Super constructor EventEmitter.call( this ); return( this ); }
Douglas Crockford también tiene algunos patrones de herencia interesantes: http://www.crockford.com/javascript/inheritance.html
Encuentro que la herencia se necesita con menos frecuencia en JavaScript y Node.js. Pero al escribir una aplicación en la que la herencia podría afectar la escalabilidad, consideraría el rendimiento contra el mantenimiento. De lo contrario, solo basaría mi decisión en qué patrones conducen a mejores diseños generales, son más fáciles de mantener y menos propensos a errores.
Pruebe diferentes patrones en jsPerf, usando Google Chrome (V8) para obtener una comparación aproximada. V8 es el motor de JavaScript utilizado por Node.js y Chrome.
Aquí hay algunos jsPerfs para comenzar:
http://jsperf.com/prototypes-vs-functions/4
http://jsperf.com/inheritance-proto-vs-object-create
http://jsperf.com/inheritance-perf
fuente
emit
yon
aparecen como indefinidos.Para agregar a la respuesta de wprl. Se perdió la parte del "prototipo":
function EventedObject(){ // Super constructor EventEmitter.call(this); return this; } EventObject.prototype = new EventEmitter(); //<-- you're missing this part
fuente
util.inherits
ya que muchas personas inteligentes mantendrán esas opciones actualizadas para usted.