Я вижу этот паттерн во многих библиотеках Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(источник здесь )
Может кто-нибудь объяснить мне на примере, почему это такой распространенный шаблон и когда он удобен?
node.js
eventemitter
Джеффривеон
источник
источник
__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');
См. Https://nodejs.org/api/events.html#events_events.
источник
require('events').EventEmitter
первую очередь - я всегда забываю. Вот ссылка на документы на случай, если это понадобится кому-то еще: nodejs.org/api/events.html#events_class_events_eventemitter )MasterInstance
должно бытьmasterInstance
.util.inherits
делает неприятную вещь, вводяsuper_
свойство вMaster
объект. В этом нет необходимости, и мы пытаемся рассматривать прототипное наследование как классическое наследование. Взгляните на нижнюю часть этой страницы для объяснения.Master.prototype = EventEmitter.prototype;
. Не нужны суперы. Вы также можете использовать ES6 extends (и это поощряется в документации Node.jsutil.inherits
) вот такclass Master extends EventEmitter
- вы получаете классическийsuper()
, но без каких-либо инъекцийMaster
.Наследование класса стиля 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()
из нее, чтобы убедиться, что конструктор родительского класса также вызывается, если у вас нет веской причины не делать этого.источник
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 :
Это тоже работает:
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
http://jsperf.com/inheritance-proto-vs-object-create
http://jsperf.com/inheritance-perf
источник
emit
иon
придумывают , как не определено.Чтобы добавить к ответу wprl. Он пропустил «прототипную» часть:
function EventedObject(){ // Super constructor EventEmitter.call(this); return this; } EventObject.prototype = new EventEmitter(); //<-- you're missing this part
источник
util.inherits
многие умные люди будут поддерживать эти параметры в актуальном состоянии.