Node.js - наследование от EventEmitter

Я вижу этот шаблон в довольно немногих библиотеках Node.js:

Master.prototype.__proto__ = EventEmitter.prototype;

(источник здесь)

Может кто-нибудь, пожалуйста, объясните мне пример, почему это такой общий шаблон и когда он удобен?

Ответ 1

Как комментарий выше этого кода говорит, он сделает 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

Документы 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(), чтобы убедиться, что также вызывается конструктор родительского класса, если только у вас нет хорошего причина не

Ответ 3

Чтобы наследовать от другого объекта Javascript, Node.js EventEmitter, в частности, но действительно любого объекта в целом, вам нужно сделать две вещи:

  • предоставляет конструктор для вашего объекта, который полностью инициализирует объект; в случае, если вы наследуете какой-либо другой объект, вы, вероятно, захотите делегировать часть этой работы по инициализации супер конструктору.
  • предоставить объект-прототип, который будет использоваться как [[proto]] для объектов, созданных из вашего конструктора; в случае, если вы наследуете какой-либо другой объект, вы, вероятно, захотите использовать экземпляр другого объекта в качестве своего прототипа.

Это сложнее в Javascript, чем может показаться на других языках, потому что

  • Javascript разделяет поведение объекта на "конструктор" и "prototype". Эти концепции предназначены для совместного использования, но могут использоваться отдельно.
  • 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);

Возможные недостатки:

  • Если вы используете набор прототипов для своего подкласса (Master.prototype), с использованием или без использования util.inherits, но не вызывайте супер-конструктор (EventEmitter) для экземпляров вашего класса, они не будут правильно инициализирован.
  • Если вы вызываете супер-конструктор, но не устанавливаете прототип, методы EventEmitter не будут работать с вашим объектом.
  • Вы можете попытаться использовать инициализированный экземпляр суперкласса (new EventEmitter) как Master.prototype вместо того, чтобы конструктор подкласса Master вызывать супер конструктор EventEmitter; в зависимости от поведения конструктора суперкласса, который может показаться, что он работает нормально какое-то время, но не то же самое (и не будет работать для EventEmitter).
  • Вы можете попытаться использовать супер прототип напрямую (Master.prototype = EventEmitter.prototype) вместо добавления дополнительного слоя объекта через Object.create; это может показаться, что он отлично работает, пока кто-то обезьяны не поместит ваш объект Master и непреднамеренно также обезврежил EventEmitter и всех остальных потомков. Каждый "класс" должен иметь свой собственный прототип.

Снова: чтобы наследовать от EventEmitter (или действительно любого существующего объекта класса), вы хотите определить конструктор, который соединяется с супер-конструктором и предоставляет прототип, который является производным от супер-прототипа.

Ответ 4

Вот как прототипическое (прототипное?) наследование выполняется в JavaScript. Из MDN:

Относится к прототипу объекта, который может быть объектом или нулевым (что обычно означает, что объект 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.

Ответ 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. Но при написании приложения, где наследование может повлиять на масштабируемость, я бы рассмотрел производительность, связанную с ремонтопригодностью. В противном случае я бы основал только мое решение о том, какие шаблоны приводят к лучшим общим проектам, более удобны в обслуживании и менее подвержены ошибкам.

Проверьте различные шаблоны в jsPerf, используя Google Chrome (V8), чтобы получить приблизительное сравнение. V8 - это механизм JavaScript, используемый как Node.js, так и Chrome.

Вот некоторые jsPerfs, чтобы вы начали:

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

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

http://jsperf.com/inheritance-perf

Ответ 6

Чтобы добавить к ответу wprl. Он пропустил часть "prototype":

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

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