Node.js - наследование от EventEmitter - PullRequest
83 голосов
/ 17 января 2012

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

Master.prototype.__proto__ = EventEmitter.prototype;

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

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

Ответы [ 6 ]

79 голосов
/ 17 января 2012

Как сказано выше в этом коде, он будет 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);
72 голосов
/ 11 марта 2016

Наследование класса стиля ES 6

Они приходят прямо из документов, но я подумал, что было бы неплохо добавить их в этот популярный вопрос для всех, кто смотрит.

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {
  constructor() {
    super(); //must call super for "this" to be defined.
  }
}

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

Я бы хотел git thank, кто бы это ни добавил. Источник событий .

Примечание: Документация не вызывает super() в конструкторе, что приведет к неопределенности this. Смотрите этот выпуск .

38 голосов
/ 28 мая 2014

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

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

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

  • Javascript разделяет поведение объекта на «конструктор» и «прототип». Эти понятия предназначены для совместного использования, но могут использоваться отдельно.
  • Javascript - это очень податливый язык, и люди используют его по-разному, и нет единого истинного определения того, что означает «наследование».
  • Во многих случаях вы можете сойтись с подмножеством того, что правильно, и вы найдете множество примеров для подражания (включая некоторые другие ответы на этот вопрос SO), которые, кажется, хорошо работают для вашего случая.

Для конкретного случая EventEmitter Node.js вот что работает:

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 (или действительно любого существующего "класса" объекта), вы хотите определить конструктор, который связан с супер-конструктором и предоставляет прототип, производный от супер-прототипа.

19 голосов
/ 17 января 2012

Вот как прототипное (прототипное) наследование осуществляется в 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 ООП - одна из лучших статей, которые я недавно читал об ООП в ECMAScript 5.

5 голосов
/ 01 марта 2013

Я думал, что этот подход из 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

1 голос
/ 03 июня 2016

Добавить к ответу wprl. Он пропустил часть «прототип»:

function EventedObject(){

   // Super constructor
   EventEmitter.call(this);

   return this;

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