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?
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?
__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');
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 ser masterInstance
.
Master.prototype = EventEmitter.prototype;
. No se necesitan alzas. También puede usar extensiones de ES6 (y se recomienda en los documentos de Node.js util.inherits
) de esta manera class Master extends EventEmitter
: obtiene clásico super()
, pero sin inyectar nada en Master
.
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 en MyEmitter
, debe llamar super()
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.
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
) en Master.prototype
lugar de que el constructor de la subclase Master
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 objeto Master
y, sin darse cuenta, también parchea EventEmitter
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.
Así es como se realiza la herencia prototípica (¿prototípica?) En JavaScript. De MDN :
Se refiere al prototipo del objeto, que puede ser un objeto o nulo (lo que generalmente significa que el objeto es Object.prototype, que no tiene prototipo). A veces se utiliza para implementar búsquedas de propiedades basadas en herencia de prototipos.
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.
Y.prototype = new X();
es un anti-patrón, por favor useY.prototype = Object.create(X.prototype);
new X()
instancia una instancia de X.prototype
y la inicializa invocándola X
. Object.create(X.prototype)
simplemente crea una instancia. No Emitter.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
emit
y on
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
util.inherits
ya que muchas personas inteligentes mantendrán esas opciones actualizadas para usted.