Widzę ten wzorzec w wielu bibliotekach Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(źródło tutaj )
Czy ktoś mógłby mi wyjaśnić na przykładzie, dlaczego jest to taki powszechny wzór i kiedy jest przydatny?
node.js
eventemitter
jeffreyveon
źródło
źródło
__proto__
to anty-wzór, użyjMaster.prototype = Object.create(EventEmitter.prototype);
util.inherits(Master, EventEmitter);
Odpowiedzi:
Jak mówi powyższy komentarz, kod ten będzie
Master
dziedziczyć zEventEmitter.prototype
, więc możesz używać instancji tej „klasy” do emitowania i nasłuchiwania zdarzeń.Na przykład możesz teraz zrobić:
masterInstance = new Master(); masterInstance.on('an_event', function () { console.log('an event has happened'); }); // trigger the event masterInstance.emit('an_event');
Aktualizacja : jak wskazało wielu użytkowników, 'standardowym' sposobem zrobienia tego w Node byłoby użycie 'util.inherits':
var EventEmitter = require('events').EventEmitter; util.inherits(Master, EventEmitter);
Druga aktualizacja : mając na uwadze klasy ES6, zaleca się
EventEmitter
teraz rozszerzenie klasy:const EventEmitter = require('events'); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on('event', () => { console.log('an event occurred!'); }); myEmitter.emit('event');
Zobacz https://nodejs.org/api/events.html#events_events
źródło
require('events').EventEmitter
trzeba zrobić najpierw - zawsze zapominam. Oto link do dokumentów na wypadek, gdyby ktoś inny tego potrzebował: nodejs.org/api/events.html#events_class_events_eventemitter )MasterInstance
powinno byćmasterInstance
.util.inherits
robi nieprzyjemną rzecz, wprowadzającsuper_
właściwość doMaster
obiektu. Jest to niepotrzebne i próbuje traktować dziedziczenie prototypowe jak klasyczne. Spójrz na dół tej strony, aby uzyskać wyjaśnienie.Master.prototype = EventEmitter.prototype;
. Nie ma potrzeby nadstawek. Możesz także używać rozszerzeń ES6 (i jest to zalecane w dokumentach Node.jsutil.inherits
) w ten sposóbclass Master extends EventEmitter
- otrzymujesz klasycznesuper()
, ale bez wstrzykiwania czegokolwiekMaster
.Dziedziczenie klas stylu ES6
Dokumentacja Node zaleca teraz używanie dziedziczenia klas do tworzenia własnego emitera zdarzeń:
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');
Uwaga: Jeśli definiujesz
constructor()
funkcję w programieMyEmitter
, powinieneś wywołaćsuper()
z niej, aby upewnić się, że konstruktor klasy nadrzędnej również zostanie wywołany, chyba że masz dobry powód, aby tego nie robić.źródło
super()
jest wymagane , o ile nie potrzebujesz / nie definiujesz konstruktora, stąd oryginalna odpowiedź Breedly (zobacz historię EDYCJI) była całkowicie poprawna. W takim przypadku możesz skopiować i wkleić ten sam przykład do repl, całkowicie usunąć konstruktora i będzie działać w ten sam sposób. To jest całkowicie poprawna składnia.Aby dziedziczyć z innego obiektu Javascript, w szczególności EventEmittera Node.js, ale w zasadzie dowolnego obiektu w ogóle, musisz zrobić dwie rzeczy:
[[proto]]
dla obiektów utworzonych z Twojego konstruktora; w przypadku, gdy dziedziczysz z innego obiektu, prawdopodobnie będziesz chciał użyć instancji innego obiektu jako prototypu.Jest to bardziej skomplikowane w JavaScript, niż mogłoby się wydawać w innych językach, ponieważ
Oto, co działa w konkretnym przypadku EventEmittera Node.js:
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);
Możliwe słabości:
util.inherits
, ale nie wywołuj super konstruktora (EventEmitter
) dla instancji Twojej klasy, nie zostaną one poprawnie zainicjowane.new EventEmitter
)Master.prototype
zamiastMaster
wywoływania konstruktora nadklasy przez konstruktor podklasyEventEmitter
; w zależności od zachowania konstruktora nadklasy, który może wydawać się, że działa dobrze przez jakiś czas, ale to nie to samo (i nie będzie działać dla EventEmitter).Master.prototype = EventEmitter.prototype
) zamiast dodawać dodatkową warstwę obiektu poprzez Object.create; może się wydawać, że działa dobrze, dopóki ktoś nie złapie twojego obiektuMaster
przez małpęEventEmitter
i nieumyślnie nie dostanie również monkeypatch i wszystkich innych jego potomków. Każda „klasa” powinna mieć swój własny prototyp.Ponownie: aby dziedziczyć po EventEmitter (lub naprawdę dowolnej istniejącej „klasie” obiektu), chcesz zdefiniować konstruktor, który łączy się w łańcuch z super konstruktorem i dostarcza prototyp wyprowadzony z super prototypu.
źródło
W ten sposób w JavaScript odbywa się dziedziczenie prototypowe (prototypowe?). Z MDN :
Działa to również:
var Emitter = function(obj) { this.obj = obj; } // DON'T Emitter.prototype = new require('events').EventEmitter(); Emitter.prototype = Object.create(require('events').EventEmitter.prototype);
Understanding JavaScript OOP to jeden z najlepszych artykułów, jakie ostatnio czytałem na temat OOP w ECMAScript 5.
źródło
Y.prototype = new X();
jest anty-wzorem, proszę użyćY.prototype = Object.create(X.prototype);
new X()
tworzy instancjęX.prototype
i inicjuje ją, wywołującX
na niej.Object.create(X.prototype)
po prostu tworzy instancję. Nie chceszEmitter.prototype
być inicjalizowany. Nie mogę znaleźć dobrego artykułu, który to wyjaśnia.Pomyślałem, że to podejście z http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm było całkiem fajne:
function EventedObject(){ // Super constructor EventEmitter.call( this ); return( this ); }
Douglas Crockford ma również kilka interesujących wzorców dziedziczenia: http://www.crockford.com/javascript/inheritance.html
Uważam, że dziedziczenie jest rzadziej potrzebne w JavaScript i Node.js. Ale pisząc aplikację, w której dziedziczenie może wpływać na skalowalność, rozważałbym wydajność ważoną względem łatwości konserwacji. W przeciwnym razie oparłbym swoją decyzję tylko na tym, które wzorce prowadzą do lepszych ogólnych projektów, są łatwiejsze w utrzymaniu i mniej podatne na błędy.
Przetestuj różne wzorce w jsPerf, używając Google Chrome (V8), aby uzyskać zgrubne porównanie. V8 to silnik JavaScript używany zarówno przez Node.js, jak i Chrome.
Oto kilka jsPerfs na początek:
http://jsperf.com/prototypes-vs-functions/4
http://jsperf.com/inheritance-proto-vs-object-create
http://jsperf.com/inheritance-perf
źródło
emit
ion
są one niezdefiniowane.Aby dodać do odpowiedzi wprl. Brakowało mu części „prototypowej”:
function EventedObject(){ // Super constructor EventEmitter.call(this); return this; } EventObject.prototype = new EventEmitter(); //<-- you're missing this part
źródło
util.inherits
ponieważ wielu inteligentnych ludzi będzie aktualizować te opcje.