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?
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?
__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ć z EventEmitter.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');
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
.
Master.prototype = EventEmitter.prototype;
. Nie ma potrzeby nadstawek. Możesz także używać rozszerzeń ES6 (i jest to zalecane w dokumentach Node.js util.inherits
) w ten sposób class Master extends EventEmitter
- otrzymujesz klasyczne super()
, ale bez wstrzykiwania czegokolwiek Master
.
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 programie MyEmitter
, 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ć.
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
zamiast Master
wywoływania konstruktora nadklasy przez konstruktor podklasy EventEmitter
; 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 obiektu Master
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.
W ten sposób w JavaScript odbywa się dziedziczenie prototypowe (prototypowe?). Z MDN :
Odnosi się do prototypu obiektu, który może być obiektem lub wartością pustą (co zwykle oznacza, że obiekt to Object.prototype, który nie ma prototypu). Czasami jest używany do implementacji wyszukiwania właściwości opartego na dziedziczeniu prototypów.
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.
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ąc X
na niej. Object.create(X.prototype)
po prostu tworzy instancję. Nie chcesz Emitter.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
emit
i on
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
util.inherits
ponieważ wielu inteligentnych ludzi będzie aktualizować te opcje.