Node.js-EventEmitterから継承


89

このパターンは、かなりの数のNode.jsライブラリで見られます。

Master.prototype.__proto__ = EventEmitter.prototype;

(ソースはこちら

誰かが例を挙げて私に説明してもらえますか、なぜこれがそのような一般的なパターンであり、それが便利なのですか?


情報については、この質問を参照してくださいstackoverflow.com/questions/5398487/…–
Juicy Scripter

14
__proto__はアンチパターンです。使用してくださいMaster.prototype = Object.create(EventEmitter.prototype);
Raynos 2012年

69
実際に使用するutil.inherits(Master, EventEmitter);
thesmart 2012年

1
@Raynosアンチパターンとは何ですか?
starbeamrainbowlabs 2015

1
これは、ES6クラスコンストラクターを使用すると簡単になりました。ここで互換性を確認してください:kangax.github.io/compat-table/es6。以下のドキュメントまたは私の回答を確認してください。
繁殖的に2016年

回答:


84

そのコードの上のコメントが言うように、それはMasterから継承するEventEmitter.prototypeので、その 'クラス'のインスタンスを使用してイベントを発行およびリッスンできます。

たとえば、次のことができます。

masterInstance = new Master();

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

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

更新:多くのユーザーが指摘しているように、Nodeでそれを行う「標準」の方法は「util.inherits」を使用することです:

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

2回目の更新:ES6クラスを使用しているため、EventEmitter今すぐクラスを拡張することをお勧めします。

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

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

myEmitter.emit('event');

https://nodejs.org/api/events.html#events_eventsを参照してください


4
require('events').EventEmitter最初に行うべきちょっとしたリマインダー-私はいつも忘れています。他の誰かがそれを必要とする場合に備えて、ドキュメントへのリンクは次のとおりです:nodejs.org/api/events.html#events_class_events_eventemitter
mikermcneil 2014

3
ところで、インスタンスの規則は最初の文字を小文字にすることなので、そうするMasterInstance必要がありますmasterInstance
khoomeister 2014

そして、次のことを確認する機能をどのように保持しますか:masterInstance instanceof Master?
jayarjo 2014年

3
util.inheritssuper_プロパティをMasterオブジェクトに注入するという厄介なことをします。それは不要であり、典型的な継承を古典的な継承のように扱います。説明については、このページの下部をご覧ください。
klh 2015年

2
@loretoparisiだけMaster.prototype = EventEmitter.prototype;です。スーパーの必要はありません。このようにES6拡張機能を使用することもできます(Node.jsのドキュメントで推奨されていますutil.inherits)。class Master extends EventEmitterクラシックsuper()になりますが、に何も挿入しませんMaster
klh 2017

81

ES6スタイルのクラス継承

Nodeのドキュメントでは、クラス継承を使用して独自のイベントエミッターを作成することを推奨しています。

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

注:でconstructor()関数を定義する場合はMyEmitter、そうしsuper()ない正当な理由がない限り、関数を呼び出して、親クラスのコンストラクターも呼び出されるようにする必要があります。


9
これらのコメントは正しくなく、この場合は誤解を招く可能性があります。呼び出しがsuper()されて必要はありません、限り、あなたは、コンストラクタを定義/必要としないようとして、それ故にBreedlyのオリジナルの答えを(編集履歴を参照してください)完全に正しかったです。この場合、これとまったく同じ例をコピーしてreplに貼り付け、コンストラクターを完全に削除すると、同じように機能します。これは完全に有効な構文です。
Aurelio 2017

39

別のJavascriptオブジェクト、特にNode.jsのEventEmitterから継承するには、実際には一般的なオブジェクトであるため、次の2つのことを行う必要があります。

  • オブジェクトを完全に初期化するコンストラクターをオブジェクトに提供します。他のオブジェクトから継承している場合は、この初期化作業の一部をスーパーコンストラクターに委任することをお勧めします。
  • [[proto]]コンストラクターから作成されたオブジェクトのforとして使用されるプロトタイプオブジェクトを提供します。他のオブジェクトから継承している場合は、他のオブジェクトのインスタンスをプロトタイプとして使用することをお勧めします。

これは、Javascriptでは他の言語よりも複雑です。

  • Javascriptは、オブジェクトの動作を「コンストラクター」と「プロトタイプ」に分けます。これらの概念は一緒に使用することを目的としていますが、別々に使用することもできます。
  • Javascriptは非常に順応性のある言語であり、人々はそれを異なる方法で使用し、「継承」が何を意味するかについての単一の真の定義はありません。
  • 多くの場合、正しいことのサブセットを実行することで逃げることができ、あなたのケースではうまくいくように見えるたくさんの例(このSOの質問に対する他のいくつかの回答を含む)を見つけることができます。

Node.jsのEventEmitterの特定のケースでは、次のように機能します。

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

考えられる失敗:

  • を使用してutil.inherits、または使用せずに、サブクラス(Master.prototype)のプロトタイプを設定するを使用するEventEmitterが、クラスのインスタンスに対してスーパーコンストラクター()を呼び出さない場合、それらは適切に初期化されません。
  • スーパーコンストラクターを呼び出したがプロトタイプを設定しない場合、EventEmitterメソッドはオブジェクトで機能しません
  • サブクラスコンストラクターにスーパーコンストラクターを呼び出させる代わりに、スーパークラス(new EventEmitter)の初期化されたインスタンスを使用しようとする場合があります。スーパークラスコンストラクターの動作によっては、しばらくは正常に機能しているように見えるかもしれませんが、同じではありません(EventEmitterでは機能しません)。Master.prototypeMasterEventEmitter
  • Master.prototype = EventEmitter.prototypeObject.createを介してオブジェクトのレイヤーを追加する代わりに、スーパープロトタイプを直接使用しようとする場合があります()。これは、誰かがオブジェクトにモンキーパッチを適用し、MasterうっかりしてモンキーパッチEventEmitterと他のすべての子孫もパッチを適用するまでは、正常に機能しているように見える場合があります。各「クラス」には、独自のプロトタイプが必要です。

繰り返しますが、EventEmitter(または実際には既存のオブジェクト「クラス」)から継承するには、スーパーコンストラクターにチェーンし、スーパープロトタイプから派生したプロトタイプを提供するコンストラクターを定義する必要があります。


19

これは、JavaScriptでプロトタイプ(プロトタイプ?)の継承が行われる方法です。MDNから:

オブジェクトのプロトタイプを参照します。これは、オブジェクトまたはnullの場合があります(通常、オブジェクトはObject.prototypeであり、プロトタイプはありません)。プロトタイプ継承ベースのプロパティルックアップを実装するために使用されることがあります。

これも同様に機能します。

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を理解することは、ECMAScript5のOOPについて最近読んだ最高の記事の1つです。


7
Y.prototype = new X();はアンチパターンです。使用してくださいY.prototype = Object.create(X.prototype);
Raynos 2012年

これは知っておくとよいでしょう。どこかでもっと読むことはできますか?結果のオブジェクトがどのように異なるかに興味があります。
ダフ2012年

4
new X()のインスタンスをインスタンス化し、X.prototypeそれを呼び出すことによって初期化しますXObject.create(X.prototype)インスタンスをインスタンス化するだけです。Emitter.prototype初期化する必要はありません。これを説明する良い記事が見つかりません。
Raynos 2012年

意味あり。ご指摘いただきありがとうございます。まだノードで良い習慣を身につけようとしています。ECMA5にはブラウザがありません(そして私が理解したように、シムは最も信頼できるものではありません)。
ダフ2012年

1
他のリンクも壊れています。この方法を試してください。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からのこのアプローチはかなりきちんとしていると思いました。

function EventedObject(){

  // Super constructor
  EventEmitter.call( this );

  return( this );

}

ダグラス・クロックフォードにもいくつかの興味深い継承パターンがあります:http//www.crockford.com/javascript/inheritance.html

JavaScriptとNode.jsでは継承が必要になることはあまりありません。しかし、継承がスケーラビリティに影響を与える可能性のあるアプリを作成する際には、パフォーマンスと保守性を比較検討します。そうでなければ、どのパターンがより良い全体的なデザインにつながり、より保守しやすく、エラーが発生しにくいかということに基づいて決定するだけです。

Google Chrome(V8)を使用して、jsPerfでさまざまなパターンをテストし、大まかな比較を行います。V8は、Node.jsとChromeの両方で使用されるJavaScriptエンジンです。

始めるためのjsPerfsは次のとおりです。

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

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

http://jsperf.com/inheritance-perf


1
私はこのアプローチと両方emitを試しましonたが、未定義として出てきています。
dopatraman 2014年

return(this)ではありません。連鎖するだけですか?
blablabla 2015年

1

wprlの応答に追加します。彼は「プロトタイプ」の部分を見逃しました:

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

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

1
実際には、newの代わりにObject.createを使用する必要があります。そうしないと、他の場所で説明されているように、プロトタイプのインスタンスの状態と動作を取得できます。しかし、ES6を使用してトランスパイルするかutil.inherits、多くの賢い人々がそれらのオプションを最新の状態に保つので、より良いです。
Cool Blue
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.