Pytanie Node.js - dziedzicząc z EventEmitter


Widzę ten wzorzec w kilku bibliotekach Node.js:

Master.prototype.__proto__ = EventEmitter.prototype;

(źródło tutaj)

Czy ktoś może mi wyjaśnić na przykładzie, dlaczego jest to tak powszechny wzór i kiedy jest przydatny?


76
2018-01-17 16:44


pochodzenie


Zobacz to pytanie, aby uzyskać informacje stackoverflow.com/questions/5398487/... - Juicy Scripter
Uwaga __proto__ to anty-wzór, użyj Master.prototype = Object.create(EventEmitter.prototype); - Raynos
Właściwie użyj util.inherits(Master, EventEmitter); - thesmart
@Raynos Co to jest anty-wzór? - starbeamrainbowlabs
Jest to teraz łatwiejsze dzięki konstruktorom klasy ES6. Sprawdź compat tutaj: kangax.github.io/compat-table/es6 . Sprawdź dokumenty lub odpowiedź poniżej. - Breedly


Odpowiedzi:


W komentarzu powyżej tego kodu będzie napisane Master dziedzicz z EventEmitter.prototype, więc możesz używać instancji tej "klasy" do emitowania i słuchania zdarzeń.

Na przykład możesz teraz:

masterInstance = new Master();

masterInstance.on('an_event', function () {
  console.log('an event has happened');
});

// trigger the event
masterInstance.emit('an_event');

Aktualizacja: jak wielu użytkowników wskazało, "standardowym" sposobem robienia tego w Węzłach byłoby użycie "util.inherits":

var EventEmitter = require('events').EventEmitter;
util.inherits(Master, EventEmitter);

77
2018-01-17 16:54



(tylko małe przypomnienie do zrobienia require('events').EventEmitter po pierwsze - zawsze zapominam. Oto link do dokumentów na wypadek, gdyby ktoś tego potrzebował: nodejs.org/api/events.html#events_class_events_eventemitter) - mikermcneil
BTW, konwencja dla instancji polega na pisaniu małymi literami pierwszej litery, więc MasterInstance powinno być masterInstance. - khoomeister
A w jaki sposób zachowujesz możliwość sprawdzenia, czy: masterInstance instanceof Master? - jayarjo
util.inherits robi nieprzyjemną rzecz wstrzykiwania super_ własność do Master obiekt. Jest to niepotrzebne i stara się traktować dziedziczenie prototypowe jak klasyczne dziedziczenie. Spójrz na dół to strona wyjaśnienia. - Killah
@loretoparisi po prostu Master.prototype = EventEmitter.prototype;. Nie potrzeba supers. Możesz także użyć rozszerzania ES6 (i jest to zalecane w dokumentach Node.js) util.inherits) lubię to class Master extends EventEmitter - dostajesz klasykę super(), ale bez wstrzykiwania niczego do Master. - Killah


Dziedziczenie klasy ES 6

Te pochodzą prosto z dokumentów, ale pomyślałem, że byłoby miło dodać je do tego popularnego pytania dla każdego, kto szuka.

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {
  constructor() {
    super(); //must call super for "this" to be defined.
  }     
}

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
  console.log('an event occurred!');
});
myEmitter.emit('event');

Chciałbym git thank ktokolwiek to dodał. Emiter zdarzeń.

Uwaga: Dokumentacja nie wywołuje super() w konstruktorze, który spowoduje this być niezdefiniowanym. Zobacz kwestia.


56
2018-03-11 16:22



`` `class MyEmitter rozszerza EventEmitter {constructor () {super (); // musi wywołać super, aby zdefiniować "to". }} `` ` - Chase Wilson
Imo najlepsza odpowiedź, ale jest błąd: super() musi być w konstruktorze. Proszę napraw to. - Firanolfind
Te komentarze nie są poprawne i kończą się wprowadzaniem w błąd w tym przypadku. Powołanie super() jest nie wymagane, o ile nie potrzebujesz / nie definiujesz konstruktora, więc oryginalna odpowiedź Breedly'a (patrz historia EDYCJI) była całkowicie poprawna. W tym przypadku możesz skopiować i wkleić ten sam przykład w repl, całkowicie usunąć konstruktora i będzie działać w ten sam sposób. To jest całkowicie poprawna składnia. - Nobita


Aby odziedziczyć inny obiekt JavaScript, EventEmitter Node.js w szczególności, ale naprawdę dowolny obiekt w ogóle, musisz zrobić dwie rzeczy:

  • dostarczyć konstruktor dla twojego obiektu, który całkowicie inicjuje obiekt; w przypadku, gdy dziedziczysz z jakiegoś innego obiektu, prawdopodobnie chcesz przekazać część tej pracy inicjującej super konstruktorowi.
  • dostarczyć prototyp obiektu, który będzie używany jako [[proto]] dla obiektów utworzonych z twojego konstruktora; w przypadku, gdy dziedziczysz z jakiegoś innego obiektu, prawdopodobnie chcesz użyć instancji innego obiektu jako swojego prototypu.

Jest to bardziej skomplikowane w JavaScript, niż mogłoby się wydawać w innych językach, ponieważ

  • JavaScript oddziela zachowanie obiektu na "konstruktor" i "prototyp". Pojęcia te mają być używane razem, ale mogą być używane osobno.
  • JavaScript jest językiem bardzo plastycznym, a ludzie używają go w inny sposób i nie ma jednej prawdziwej definicji tego, co oznacza "dziedziczenie".
  • W wielu przypadkach można uciec, wykonując podzbiór, co jest poprawne, a znajdziesz mnóstwo przykładów do naśladowania (w tym kilka innych odpowiedzi na to pytanie), które wydają się działać dobrze dla twojej sprawy.

W przypadku konkretnego zdarzenia EventEmitter Node.js, oto co działa:

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:

  • Jeśli użyjesz ustawić prototyp twojej podklasy (Master.prototype), z lub bez użycia util.inherits, ale nie dzwoń do super-konstruktora (EventEmitter) dla instancji klasy, nie zostaną poprawnie zainicjowane.
  • Jeśli wywołasz super konstruktora, ale nie ustawisz prototypu, metody EventEmitter nie będą działać na twoim obiekcie
  • Możesz spróbować użyć inicjowanej instancji superklasy (new EventEmitter) tak jak Master.prototype zamiast konstruktora podklasy Master zadzwoń do super-konstruktora EventEmitter; w zależności od zachowania konstruktora nadklasy, który może wydawać się, że działa dobrze przez jakiś czas, ale nie jest tym samym (i nie będzie działać dla EventEmitter).
  • Możesz spróbować użyć super prototypu bezpośrednio (Master.prototype = EventEmitter.prototype) zamiast dodawać dodatkową warstwę obiektu poprzez Object.create; może się wydawać, że działa dobrze, dopóki ktoś nie podpisze twojego obiektu Master i nieumyślnie również monkeypatched EventEmitter i wszystkich jego pozostałych potomków. Każda "klasa" powinna mieć swój własny prototyp.

Ponownie: aby dziedziczyć z EventEmitter (lub naprawdę dowolnego istniejącego obiektu "class"), chcesz zdefiniować konstruktora, który łączy się z super konstruktorem i dostarcza prototyp, który pochodzi od super prototypu.


36
2018-05-28 17:55





W ten sposób dziedziczenie prototypowe (prototypowe?) Odbywa się w JavaScript. Od MDN:

Odwołuje 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   prototyp). Czasami służy do implementacji dziedziczenia prototypów   oparte wyszukiwanie właściwości.

To również działa:

var Emitter = function(obj) {
    this.obj = obj;
}

// DON'T Emitter.prototype = new require('events').EventEmitter();
Emitter.prototype = Object.create(require('events').EventEmitter.prototype);

Zrozumienie JavaScript OOP jest jednym z najlepszych artykułów, które ostatnio czytałem na temat OOP w ECMAScript 5.


19
2018-01-17 16:55



Y.prototype = new X(); to anty-wzór, użyj Y.prototype = Object.create(X.prototype); - Raynos
Dobrze to wiedzieć. Czy mogę gdzieś czytać dalej? Byłby zainteresowany, w jaki sposób powstałe obiekty są różne. - Daff
new X() stwórz instancję X.prototype i inicjuje go przez wywołanie X na tym. Object.create(X.prototype) po prostu tworzy instancję. Nie powinieneś Emitter.prototype do zainicjowania. Nie mogę znaleźć dobrego artykułu, który to wyjaśnia. - Raynos
To ma sens. Dziękuję za wskazanie. Nadal próbuję wejść w dobre nawyki na Węzłach. Przeglądarki nie są dostępne dla ECMA5 (i jak rozumiem, podkładka nie jest najbardziej niezawodna). - Daff
Drugi link również jest zepsuty. Spróbuj tego: robotlolita.github.io/2011/10/09/... - jlukanta


Myślałem, że to podejście http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm było całkiem schludne:

function EventedObject(){

  // Super constructor
  EventEmitter.call( this );

  return( this );

}

Douglas Crockford ma również kilka interesujących wzoró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 ważność wydajności pod względem łatwości konserwacji. W przeciwnym razie opierałbym swoją decyzję na tym, które wzory prowadzą do lepszych ogólnych projektów, są łatwiejsze do utrzymania i mniej podatne na błędy.

Przetestuj różne wzory w jsPerf, używając przeglądarki Google Chrome (V8), aby uzyskać przybliżone porównanie. V8 to silnik JavaScript używany zarówno przez Node.js, jak i Chrome.

Oto kilka jsPerfs, od których możesz zacząć:

http://jsperf.com/prototypes-vs-functions/4

http://jsperf.com/inheritance-proto-vs-object-create

http://jsperf.com/inheritance-perf


5
2018-03-01 16:50



Próbowałem tego podejścia i jedno i drugie emit i on nadchodzą jako niezdefiniowane. - dopatraman
To nie jest powrót (to); tylko do łączenia? - blablabla


Aby dodać do odpowiedzi wprl. Brakowało części "prototypowej":

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part

1
2018-06-03 20:47



Właściwie powinieneś użyć Object.create zamiast nowego, inaczej uzyskasz stan instancji, jak również zachowanie na prototypie, jak wyjaśniono gdzie indziej. Ale lepiej używać ES6 i transpile lub util.inherits ponieważ wielu inteligentnych ludzi sprawi, że te opcje będą dla ciebie aktualne. - Cool Blue