Node.js-EventEmitter에서 상속


89

꽤 많은 Node.js 라이브러리에서이 패턴을 볼 수 있습니다.

Master.prototype.__proto__ = EventEmitter.prototype;

( 여기 출처 )

누군가가 예를 들어 설명해 주시겠습니까? 이것이 왜 그렇게 흔한 패턴이며 언제 유용합니까?


정보에 대한이 질문을 참조하십시오 stackoverflow.com/questions/5398487/...
달콤한 스크립터

14
참고 __proto__는 안티 패턴입니다. 사용하십시오Master.prototype = Object.create(EventEmitter.prototype);
Raynos

69
실제로 사용util.inherits(Master, EventEmitter);
thesmart 2012

1
@Raynos 안티 패턴이란 무엇입니까?
starbeamrainbowlabs

1
이제 ES6 클래스 생성자를 사용하면 더 쉽습니다. 여기에서 compat를 확인하십시오 : kangax.github.io/compat-table/es6 . 아래 문서 또는 내 대답을 확인하십시오.
Breedly

답변:


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
BTW, 인스턴스 규칙은 첫 글자를 소문자로하는 MasterInstance것이므로 masterInstance.
khoomeister 2014

그리고 masterInstance instanceof Master를 확인하는 기능을 어떻게 유지합니까?
jayarjo 2014.05.17

3
util.inherits객체에 super_속성을 주입하는 불쾌한 일을 Master합니다. 불필요하며 원형 상속을 고전적인 상속처럼 취급하려고합니다. 설명 은 페이지 하단을 참조하십시오.
klh

2
@loretoparisi 그냥 Master.prototype = EventEmitter.prototype;. 수퍼가 필요 없습니다. 또한 ES6가 확장 사용할 수 있습니다 (그것은에서 Node.js를 워드 프로세서에 권장 것 util.inherits)이 같은 class Master extends EventEmitter- 당신은 고전받을 super()만에 아무것도 주입하지 않고 Master.
klh

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

39

다른 자바 스크립트 객체, 특히 Node.js의 EventEmitter (특히 일반적으로 모든 객체)에서 상속하려면 두 가지 작업을 수행해야합니다.

  • 객체를 완전히 초기화하는 생성자를 제공합니다. 다른 객체에서 상속하는 경우이 초기화 작업의 일부를 수퍼 생성자에 위임 할 수 있습니다.
  • [[proto]]생성자에서 생성 된 객체의 으로 사용될 프로토 타입 객체를 제공합니다 . 다른 개체에서 상속하는 경우 다른 개체의 인스턴스를 프로토 타입으로 사용하고 싶을 것입니다.

이것은 다른 언어에서 보이는 것보다 자바 스크립트에서 더 복잡합니다.

  • 자바 스크립트는 객체 동작을 "생성자"와 "프로토 타입"으로 분리합니다. 이러한 개념은 함께 사용하기위한 것이지만 별도로 사용할 수 있습니다.
  • 자바 스크립트는 매우 유연한 언어이며 사람들은이를 다르게 사용하며 "상속"이 무엇을 의미하는지에 대한 진정한 정의는 없습니다.
  • 대부분의 경우 올바른 부분의 하위 집합을 수행하여 벗어날 수 있으며 귀하의 경우에 잘 작동하는 것으로 보이는 수많은 예제 (이 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);

가능한 허점 :

  • 을 사용하거나 사용하지 않고 하위 클래스 (Master.prototype)에 대한 프로토 타입을 설정 util.inherits하지만 EventEmitter클래스 인스턴스에 대해 슈퍼 생성자 ( )를 호출하지 않으면 제대로 초기화되지 않습니다.
  • 슈퍼 생성자를 호출하지만 프로토 타입을 설정하지 않으면 EventEmitter 메서드가 개체에서 작동하지 않습니다.
  • 하위 클래스 생성자 가 수퍼 생성자를 호출하는 대신 수퍼 클래스 ( new EventEmitter) 의 초기화 된 인스턴스를 사용하려고 할 수 있습니다 . 잠시 동안 잘 작동하는 것처럼 보일 수 있지만 동일한 것은 아닙니다 (EventEmitter에서는 작동하지 않음).Master.prototypeMasterEventEmitter
  • Master.prototype = EventEmitter.prototypeObject.create를 통해 객체의 추가 레이어를 추가하는 대신 수퍼 프로토 타입 ( )을 직접 사용하려고 할 수 있습니다 . 누군가가 당신의 객체 Master를 몽키 패치 EventEmitter하고 우연히 몽키 패치 와 다른 모든 자손을 가질 때까지 잘 작동하는 것처럼 보일 수 있습니다 . 각 "클래스"에는 자체 프로토 타입이 있어야합니다.

다시 말하지만, EventEmitter (또는 실제로 기존 객체 "클래스")에서 상속하려면 수퍼 생성자에 연결하고 수퍼 프로토 타입에서 파생 된 프로토 타입을 제공하는 생성자를 정의해야합니다.


19

이것이 자바 스크립트에서 프로토 타입 (prototypal?) 상속이 수행되는 방식입니다. 에서 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 이해 는 최근에 ECMAScript 5에서 OOP에 대해 읽은 최고의 기사 중 하나입니다.


7
Y.prototype = new X();안티 패턴입니다. 사용하십시오Y.prototype = Object.create(X.prototype);
Raynos

알아두면 좋습니다. 어디에서 더 읽을 수 있습니까? 결과 개체가 어떻게 다른지 관심이있을 것입니다.
Daff

4
new X()의 인스턴스를 인스턴스화하고 X.prototype이를 호출 X하여 초기화 합니다. Object.create(X.prototype)인스턴스를 인스턴스화합니다. Emitter.prototype초기화를 원하지 않습니다 . 이것을 설명하는 좋은 기사를 찾을 수 없습니다.
Raynos

이것은 의미가 있습니다. 지적 해 주셔서 감사합니다. 여전히 Node.js에서 좋은 습관을들이려고 노력하고 있습니다. 브라우저는 ECMA5를 위해 존재하지 않습니다 (그리고 shim이 가장 신뢰할 수 없다는 것을 이해했듯이).
Daff

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

}

Douglas Crockford에도 몇 가지 흥미로운 상속 패턴이 있습니다. 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
나는이 방법을 모두 시도했습니다 emiton정의되지 않은으로 올라오고있다.
dopatraman

반환 (this); 연결을 위해?
blablabla

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와 transpile을 사용하는 것이 더 낫거나 util.inherits많은 똑똑한 사람들이 이러한 옵션을 최신 상태로 유지하기 때문입니다.
Cool Blue
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.