Я вижу этот паттерн во многих библиотеках Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(источник здесь )
Может кто-нибудь объяснить мне на примере, почему это такой распространенный шаблон и когда он удобен?
Я вижу этот паттерн во многих библиотеках Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(источник здесь )
Может кто-нибудь объяснить мне на примере, почему это такой распространенный шаблон и когда он удобен?
__proto__
- это Master.prototype = Object.create(EventEmitter.prototype);
util.inherits(Master, EventEmitter);
Ответы:
Как говорится в комментарии к этому коду, он будет 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');
require('events').EventEmitter
первую очередь - я всегда забываю. Вот ссылка на документы на случай, если это понадобится кому-то еще: nodejs.org/api/events.html#events_class_events_eventemitter )
MasterInstance
должно быть masterInstance
.
Master.prototype = EventEmitter.prototype;
. Не нужны суперы. Вы также можете использовать ES6 extends (и это поощряется в документации Node.js util.inherits
) вот так class Master extends EventEmitter
- вы получаете классический super()
, но без каких-либо инъекций Master
.
В документации по 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()
из нее, чтобы убедиться, что конструктор родительского класса также вызывается, если у вас нет веской причины не делать этого.
super()
будет не требуется , до тех пор , пока вы не нужны / определить конструктор, поэтому оригинальный ответ Breedly (см истории EDIT) был совершенно правильным. В этом случае вы можете скопировать и вставить этот же пример в ответ, полностью удалить конструктор, и он будет работать так же. Это совершенно правильный синтаксис.
Чтобы наследовать от другого объекта Javascript, в частности от EventEmitter Node.js, но на самом деле от любого объекта в целом, вам необходимо сделать две вещи:
[[proto]]
объекта for, созданного из вашего конструктора; в случае, если вы наследуете от какого-то другого объекта, вы, вероятно, захотите использовать экземпляр другого объекта в качестве своего прототипа.В Javascript это сложнее, чем может показаться на других языках, потому что
Для конкретного случая 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);
Возможные недостатки:
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 - одна из лучших статей, которые я читал за последнее время по ООП в ECMAScript 5.
Y.prototype = new X();
это Y.prototype = Object.create(X.prototype);
new X()
создает экземпляр X.prototype
и инициализирует его, вызывая X
на нем. Object.create(X.prototype)
просто создает экземпляр. Вы не хотите, Emitter.prototype
чтобы вас инициализировали. Я не могу найти хорошую статью, объясняющую это.
Я думал, что этот подход из http://www.bennadel.com/blog/2187-Exnding-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.
Вот несколько jsPerf для начала:
http://jsperf.com/prototypes-vs-functions/4
emit
и on
придумывают , как не определено.
Чтобы добавить к ответу wprl. Он пропустил «прототипную» часть:
function EventedObject(){
// Super constructor
EventEmitter.call(this);
return this;
}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part
util.inherits
многие умные люди будут поддерживать эти параметры в актуальном состоянии.