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