Vedo questo modello in parecchie librerie Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(fonte qui )
Qualcuno può spiegarmi con un esempio, perché questo è un modello così comune e quando è utile?
Vedo questo modello in parecchie librerie Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(fonte qui )
Qualcuno può spiegarmi con un esempio, perché questo è un modello così comune e quando è utile?
__proto__
è un anti-pattern, si prega di utilizzareMaster.prototype = Object.create(EventEmitter.prototype);
util.inherits(Master, EventEmitter);
Risposte:
Come dice il commento sopra quel codice, Master
erediterà da EventEmitter.prototype
, quindi puoi usare istanze di quella "classe" per emettere e ascoltare eventi.
Ad esempio ora potresti fare:
masterInstance = new Master();
masterInstance.on('an_event', function () {
console.log('an event has happened');
});
// trigger the event
masterInstance.emit('an_event');
Aggiornamento : come molti utenti hanno sottolineato, il modo 'standard' di farlo in Node sarebbe usare 'util.inherits':
var EventEmitter = require('events').EventEmitter;
util.inherits(Master, EventEmitter);
2 ° aggiornamento : con le classi ES6 su di noi, si consiglia di estendere la EventEmitter
classe ora:
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
prima - dimentico sempre. Ecco il link ai documenti nel caso qualcun altro ne abbia bisogno: nodejs.org/api/events.html#events_class_events_eventemitter )
MasterInstance
dovrebbe essere masterInstance
.
Master.prototype = EventEmitter.prototype;
. Non c'è bisogno di super. Puoi anche usare le estensioni ES6 (ed è incoraggiato nei documenti di Node.js su util.inherits
) in questo modo class Master extends EventEmitter
: ottieni il classico super()
, ma senza iniettare nulla in Master
.
La documentazione di Node ora consiglia di utilizzare l'ereditarietà della classe per creare il proprio emettitore di eventi:
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: se definisci una constructor()
funzione in MyEmitter
, dovresti chiamare super()
da essa per assicurarti che venga chiamato anche il costruttore della classe genitore, a meno che tu non abbia una buona ragione per non farlo.
super()
è richiesta , a condizione che non sia necessario / definire un costruttore, quindi la risposta originale di Breedly (vedere la cronologia EDIT) era del tutto corretta. In questo caso puoi copiare e incollare questo stesso esempio nella sostituzione, rimuovere completamente il costruttore e funzionerà allo stesso modo. Questa è una sintassi perfettamente valida.
Per ereditare da un altro oggetto Javascript, in particolare l'EventEmitter di Node.js, ma in realtà qualsiasi oggetto in generale, devi fare due cose:
[[proto]]
oggetto for creato dal costruttore; nel caso in cui tu stia ereditando da qualche altro oggetto, probabilmente vorrai utilizzare un'istanza dell'altro oggetto come prototipo.Questo è più complicato in Javascript di quanto potrebbe sembrare in altre lingue perché
Per il caso specifico di EventEmitter di Node.js, ecco cosa funziona:
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);
Possibili svantaggi:
util.inherits
, ma non chiami il super costruttore ( EventEmitter
) per le istanze della tua classe, non verranno inizializzate correttamente.new EventEmitter
) Master.prototype
invece di fare in modo che il costruttore della sottoclasse Master
chiami il super costruttore EventEmitter
; a seconda del comportamento del costruttore della superclasse che potrebbe sembrare che funzioni bene per un po ', ma non è la stessa cosa (e non funzionerà per EventEmitter).Master.prototype = EventEmitter.prototype
) invece di aggiungere un ulteriore livello di oggetto tramite Object.create; questo potrebbe sembrare che funzioni bene fino a quando qualcuno non prende il tuo oggetto Master
e inavvertitamente ha anche applicato il patch di scimmia EventEmitter
e tutti i suoi discendenti. Ogni "classe" dovrebbe avere il proprio prototipo.Ancora una volta: per ereditare da EventEmitter (o in realtà qualsiasi "classe" di oggetti esistente), si desidera definire un costruttore che si concatena al super costruttore e fornisce un prototipo derivato dal super prototipo.
Questo è il modo in cui l'ereditarietà prototipica (prototipica?) Viene eseguita in JavaScript. Da MDN :
Si riferisce al prototipo dell'oggetto, che può essere un oggetto o null (che di solito significa che l'oggetto è Object.prototype, che non ha prototipo). A volte viene utilizzato per implementare la ricerca di proprietà basata sull'ereditarietà del prototipo.
Funziona anche questo:
var Emitter = function(obj) {
this.obj = obj;
}
// DON'T Emitter.prototype = new require('events').EventEmitter();
Emitter.prototype = Object.create(require('events').EventEmitter.prototype);
Capire JavaScript OOP è uno dei migliori articoli che ho letto ultimamente su OOP in ECMAScript 5.
Y.prototype = new X();
è un anti-pattern, si prega di utilizzareY.prototype = Object.create(X.prototype);
new X()
crea un'istanza di X.prototype
e la inizializza invocandola X
. Object.create(X.prototype)
crea solo un'istanza. Non vuoi Emitter.prototype
essere inizializzato. Non riesco a trovare un buon articolo che spieghi questo.
Ho pensato che questo approccio da http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm fosse abbastanza pulito:
function EventedObject(){
// Super constructor
EventEmitter.call( this );
return( this );
}
Douglas Crockford ha anche alcuni interessanti modelli di ereditarietà: http://www.crockford.com/javascript/inheritance.html
Trovo che l'ereditarietà sia meno spesso necessaria in JavaScript e Node.js. Ma nello scrivere un'app in cui l'ereditarietà potrebbe influire sulla scalabilità, considererei le prestazioni valutate rispetto alla manutenibilità. Altrimenti, baserei la mia decisione solo su quali modelli portano a progetti complessivi migliori, sono più gestibili e meno soggetti a errori.
Prova diversi modelli in jsPerf, utilizzando Google Chrome (V8) per ottenere un confronto approssimativo. V8 è il motore JavaScript utilizzato sia da Node.js che da Chrome.
Ecco alcuni jsPerfs per iniziare:
http://jsperf.com/prototypes-vs-functions/4
emit
e on
risultano indefiniti.
Da aggiungere alla risposta di wprl. Gli mancava la parte "prototipo":
function EventedObject(){
// Super constructor
EventEmitter.call(this);
return this;
}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part