[node.js] Node.js-EventEmitter에서 상속

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

Master.prototype.__proto__ = EventEmitter.prototype;

( 여기 출처 )

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



답변

해당 코드 위의 주석에서 알 수 있듯이에서 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를


답변

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()합니다.


답변

다른 자바 스크립트 객체, 특히 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 (또는 실제로 기존 객체 “클래스”)에서 상속하려면 수퍼 생성자에 연결하고 수퍼 프로토 타입에서 파생 된 프로토 타입을 제공하는 생성자를 정의해야합니다.


답변

이것이 자바 스크립트에서 프로토 타입 (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에 대해 읽은 최고의 기사 중 하나입니다.


답변

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


답변

wprl의 응답에 추가합니다. 그는 “프로토 타입”부분을 놓쳤습니다.

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

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


답변