Node.js - EventEmitter'dan devralma


89

Bu kalıbı epeyce Node.js kitaplığında görüyorum:

Master.prototype.__proto__ = EventEmitter.prototype;

(kaynak burada )

Birisi bana bir örnekle açıklayabilir mi, bunun neden bu kadar yaygın bir model olduğunu ve ne zaman kullanışlı olduğunu?



14
Not __proto__bir anti-modeldir, lütfen kullanınMaster.prototype = Object.create(EventEmitter.prototype);
Raynos

69
Aslında şunu kullanınutil.inherits(Master, EventEmitter);
themart

1
@Raynos Anti-model nedir?
starbeamrainbowlabs

1
ES6 Sınıfı oluşturucularla bu artık daha kolay. Uyumluluğu buradan kontrol edin: kangax.github.io/compat-table/es6 . Aşağıdaki belgeleri veya cevabımı kontrol edin.
Breedly

Yanıtlar:


84

Kodun yukarısındaki yorumda belirtildiği gibi, bu koddan Mastermiras alacak EventEmitter.prototype, böylece olayları yaymak ve dinlemek için bu 'sınıfın' örneklerini kullanabilirsiniz.

Örneğin şimdi yapabilirsiniz:

masterInstance = new Master();

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

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

Güncelleme : Birçok kullanıcının belirttiği gibi, bunu Node'da yapmanın 'standart' yolu 'util.inherits' kullanmak olacaktır:

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

2. Güncelleme : ES6 sınıfları ile birlikte, EventEmittersınıfı şimdi genişletmeniz önerilir :

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

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

myEmitter.emit('event');

Bkz. Https://nodejs.org/api/events.html#events_events


4
(sadece küçük bir hatırlatma - require('events').EventEmitterher zaman unutuyorum. Başkasının ihtiyacı olması durumunda dokümanlar için bağlantı burada: nodejs.org/api/events.html#events_class_events_eventemitter )
mikermcneil

3
BTW, örnekler için kural, ilk harfi küçük harf yapmaktır, bu yüzden MasterInstanceolmalıdır masterInstance.
khoomeister

Ve aşağıdakilerin olup olmadığını nasıl kontrol edebilirsiniz: masterInstance Masterof?
jayarjo

3
util.inheritssuper_özelliği Masternesneye enjekte etmek gibi kötü bir şey yapar . Gereksizdir ve prototip kalıtımı klasik kalıtım gibi ele almaya çalışır. Açıklama için bu sayfanın altına bir göz atın .
klh

2
@loretoparisi sadece Master.prototype = EventEmitter.prototype;. Süperlere gerek yok. ES6 uzantılarını da bu şekilde kullanabilirsiniz (ve Node.js belgelerinde teşvik edilmektedir util.inherits) class Master extends EventEmitter- klasik olursunuz super(), ancak içine herhangi bir şey enjekte etmeden Master.
klh

81

ES6 Stil Sınıfı Kalıtımı

Düğüm belgeleri artık kendi olay yayıcınızı oluşturmak için sınıf mirasını kullanmanızı öneriyor :

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');

Not:constructor() içinde bir işlev tanımlarsanız MyEmitter, bunu super()yapmamak için iyi bir nedeniniz yoksa, ana sınıfın kurucusunun da çağrılmasını sağlamak için ondan çağırmalısınız .


9
Bu yorumlar doğru değildir ve bu durumda yanıltıcıdır. Bir kurucuya ihtiyaç duymadığınız / tanımlamadığınız sürece arama super()yapmak gerekli değildir, bu nedenle Breedly'nin orijinal cevabı (bkz.DÜZENLE geçmişi) tamamen doğrudur. Bu durumda, bu aynı örneği kopyaya kopyalayıp yapıştırabilir, kurucuyu tamamen kaldırabilirsiniz ve aynı şekilde çalışacaktır. Bu mükemmel bir sözdizimidir.
Aurelio

39

Başka bir Javascript nesnesinden, özellikle Node.js'nin EventEmiter'ından, ancak gerçekten herhangi bir nesneden miras almak için iki şey yapmanız gerekir:

  • nesneniz için nesneyi tamamen başlatan bir kurucu sağlayın; başka bir nesneden miras aldığınız durumda, muhtemelen bu başlatma çalışmasının bir kısmını süper kurucuya devretmek istersiniz.
  • [[proto]]yapıcınızdan oluşturulan nesneler için kullanılacak bir prototip nesnesi sağlayın; Başka bir nesneden miras aldığınız durumda, muhtemelen prototipiniz olarak diğer nesnenin bir örneğini kullanmak istersiniz.

Bu, Javascript'te diğer dillerde göründüğünden daha karmaşıktır çünkü

  • Javascript, nesne davranışını "yapıcı" ve "prototip" olarak ayırır. Bu kavramların birlikte kullanılması amaçlanmıştır, ancak ayrı olarak kullanılabilir.
  • Javascript oldukça esnek bir dildir ve insanlar onu farklı şekilde kullanır ve "kalıtım" ın ne anlama geldiğine dair tek bir gerçek tanım yoktur.
  • Çoğu durumda, doğru olanın bir alt kümesini yapmaktan kurtulabilirsiniz ve davanız için iyi sonuç veren izlenecek tonlarca örnek (bu SO sorusuna verilen diğer bazı yanıtlar dahil) bulacaksınız.

Node.js'nin EventEmiter'ının özel durumu için şu şekilde çalışır:

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);

Olası zayıflıklar:

  • Kullanarak veya kullanmadan util.inherits, alt sınıfınız için prototipi (Master.prototype) belirlerseniz, ancak EventEmittersınıfınızın örnekleri için süper yapıcıyı () çağırmazsanız , bunlar düzgün şekilde başlatılmaz.
  • Süper yapıcıyı çağırır ancak prototipi ayarlamazsanız, EventEmitter yöntemleri nesneniz üzerinde çalışmaz
  • Alt sınıf yapıcısının süper yapıcıyı çağırması yerine , süper sınıfın ( new EventEmitter) başlatılmış bir örneğini kullanmayı deneyebilirsiniz ; bir süre iyi çalışıyormuş gibi görünen ancak aynı şey olmayan (ve EventEmitter için çalışmayacak) süper sınıf kurucusunun davranışına bağlı olarak.Master.prototypeMasterEventEmitter
  • Master.prototype = EventEmitter.prototypeObject.create aracılığıyla ek bir nesne katmanı eklemek yerine süper prototipi doğrudan ( ) kullanmayı deneyebilirsiniz; Bu, birisi nesnenizi Mastermaymunla eşleştirene EventEmitterve istemeden maymun eki ve diğer tüm soyundan gelenlere kadar iyi çalışıyormuş gibi görünebilir . Her "sınıf" kendi prototipine sahip olmalıdır.

Yine: EventEmitter'dan (veya gerçekten mevcut herhangi bir nesne "sınıfından") miras almak için, süper kurucuyu zincirleyen ve süper prototipten türetilen bir prototip sağlayan bir kurucu tanımlamak istersiniz.


19

JavaScript'te prototip (prototip?) Kalıtım bu şekilde yapılır. Gönderen MDN'yi :

Nesne veya boş olabilen nesnenin prototipini ifade eder (bu genellikle nesnenin prototipi olmayan Object.prototype olduğu anlamına gelir). Bazen prototip mirasına dayalı özellik aramasını uygulamak için kullanılır.

Bu da işe yarar:

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

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

JavaScript OOP'yi anlamak , ECMAScript 5'te OOP'ta son zamanlarda okuduğum en iyi makalelerden biridir.


7
Y.prototype = new X();bir anti-desen, lütfen kullanınY.prototype = Object.create(X.prototype);
Raynos

Bunu bilmek güzel. Bir yerde daha fazla okuyabilir miyim? Ortaya çıkan nesnelerin nasıl farklı olduğu ilgilenirdi.
Daff

4
new X()bir örneğini somutlaştırır X.prototypeve onu çağırarak onu başlatır X. Object.create(X.prototype)sadece bir örnek oluşturur. İlklendirilmek istemezsin Emitter.prototype. Bunu açıklayan iyi bir makale bulamıyorum.
Raynos

Bu mantıklı. Gösterdiğiniz için teşekkürler. Hala Node'da iyi alışkanlıklar edinmeye çalışıyorum. Tarayıcılar sadece ECMA5 için orada değil (ve anladığım kadarıyla shim en güvenilir değil).
Daff

1
Diğer bağlantı da kopmuştur. Bunu deneyin: robotlolita.github.io/2011/10/09/…
jlukanta

5

Http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm'deki bu yaklaşımın oldukça düzgün olduğunu düşündüm :

function EventedObject(){

  // Super constructor
  EventEmitter.call( this );

  return( this );

}

Douglas Crockford da bazı ilginç kalıtım kalıplarına sahiptir: http://www.crockford.com/javascript/inheritance.html

JavaScript ve Node.js'de kalıtıma daha az ihtiyaç duyulduğunu görüyorum. Ancak, kalıtımın ölçeklenebilirliği etkileyebileceği bir uygulama yazarken, performansın sürdürülebilirliğe karşı tartıldığını düşünürdüm. Aksi takdirde, kararımı yalnızca hangi modellerin daha iyi genel tasarımlara yol açtığına, daha sürdürülebilir olduğuna ve daha az hata yapmaya yatkın olduğuna dayandırırım.

Kabaca bir karşılaştırma elde etmek için Google Chrome'u (V8) kullanarak jsPerf'de farklı desenleri test edin. V8, hem Node.js hem de Chrome tarafından kullanılan JavaScript motorudur.

İşte başlamanıza yardımcı olacak bazı jsPerfs:

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

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

http://jsperf.com/inheritance-perf


1
Ben bu yaklaşımı ve her iki denedim emitve ontanımlanmamış olarak geliyor.
dopatraman

Geri dönüş değil (bu); sadece zincirleme için mi?
blablabla

1

Wprl'nin cevabına eklemek için. "Prototip" kısmını kaçırdı:

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

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

1
Aslında, new yerine Object.create kullanmalısınız, aksi takdirde başka bir yerde açıklandığı gibi örnek durumunu ve prototip üzerindeki davranışı alırsınız . Ancak ES6 ve transpile kullanmak daha iyidir, util.inheritsçünkü birçok akıllı insan bu seçenekleri sizin için güncel tutacaktır.
Mavi Soğuk
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.