Я вижу этот шаблон в довольно немногих библиотеках Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(источник здесь)
Может кто-нибудь, пожалуйста, объясните мне пример, почему это такой общий шаблон и когда он удобен?
Я вижу этот шаблон в довольно немногих библиотеках 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);
Документы 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()
, чтобы убедиться, что также вызывается конструктор родительского класса, если только у вас нет хорошего причина не
Чтобы наследовать от другого объекта Javascript, Node.js EventEmitter, в частности, но действительно любого объекта в целом, вам нужно сделать две вещи:
[[proto]]
для объектов, созданных из вашего конструктора; в случае, если вы наследуете какой-либо другой объект, вы, вероятно, захотите использовать экземпляр другого объекта в качестве своего прототипа.Это сложнее в Javascript, чем может показаться на других языках, потому что
Для конкретного случая 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);
Возможные недостатки:
util.inherits
, но не вызывайте супер-конструктор (EventEmitter
) для экземпляров вашего класса, они не будут правильно инициализирован.new EventEmitter
) как Master.prototype
вместо того, чтобы конструктор подкласса Master
вызывать супер конструктор EventEmitter
; в зависимости от поведения конструктора суперкласса, который может показаться, что он работает нормально какое-то время, но не то же самое (и не будет работать для EventEmitter).Master.prototype = EventEmitter.prototype
) вместо добавления дополнительного слоя объекта через Object.create; это может показаться, что он отлично работает, пока кто-то обезьяны не поместит ваш объект Master
и непреднамеренно также обезврежил EventEmitter
и всех остальных потомков. Каждый "класс" должен иметь свой собственный прототип.Снова: чтобы наследовать от EventEmitter (или действительно любого существующего объекта класса), вы хотите определить конструктор, который соединяется с супер-конструктором и предоставляет прототип, который является производным от супер-прототипа.
Вот как прототипическое (прототипное?) наследование выполняется в 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.
Я думал, что этот подход от 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
Чтобы добавить к ответу wprl. Он пропустил часть "prototype":
function EventedObject(){
// Super constructor
EventEmitter.call(this);
return this;
}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part