Vejo esse padrão em algumas bibliotecas Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(fonte aqui )
Alguém pode me explicar com um exemplo, por que esse padrão é tão comum e quando é útil?
Vejo esse padrão em algumas bibliotecas Node.js:
Master.prototype.__proto__ = EventEmitter.prototype;
(fonte aqui )
Alguém pode me explicar com um exemplo, por que esse padrão é tão comum e quando é útil?
__proto__
é um anti-padrão, useMaster.prototype = Object.create(EventEmitter.prototype);
util.inherits(Master, EventEmitter);
Respostas:
Como diz o comentário acima daquele código, ele fará Master
herdar de EventEmitter.prototype
, então você pode usar instâncias dessa 'classe' para emitir e ouvir eventos.
Por exemplo, agora você pode fazer:
masterInstance = new Master();
masterInstance.on('an_event', function () {
console.log('an event has happened');
});
// trigger the event
masterInstance.emit('an_event');
Atualização : como muitos usuários apontaram, a maneira 'padrão' de fazer isso no Node seria usar 'util.inherits':
var EventEmitter = require('events').EventEmitter;
util.inherits(Master, EventEmitter);
2ª atualização : com as aulas ES6 disponíveis, é recomendado estender a EventEmitter
aula agora:
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
primeiro - eu sempre esqueço. Aqui está o link para os documentos, caso alguém mais precise: nodejs.org/api/events.html#events_class_events_eventemitter )
MasterInstance
deveria ser masterInstance
.
Master.prototype = EventEmitter.prototype;
. Não há necessidade de supers. Você também pode usar extensões ES6 (e isso é incentivado na documentação do Node.js util.inherits
) assim class Master extends EventEmitter
- você obtém o clássico super()
, mas sem injetar nada em Master
.
Os documentos do Node agora recomendam o uso de herança de classe para fazer seu próprio emissor de evento:
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');
Observação: se você definir uma constructor()
função em MyEmitter
, deverá chamá super()
-la a partir dela para garantir que o construtor da classe pai também seja chamado, a menos que tenha um bom motivo para não fazê-lo.
super()
é necessária , contanto que você não precise / defina um construtor, portanto, a resposta original de Breedly (consulte o histórico de EDITAR) foi totalmente correta. Neste caso, você pode copiar e colar esse mesmo exemplo no repl, remover o construtor inteiramente e tudo funcionará da mesma maneira. Essa é uma sintaxe perfeitamente válida.
Para herdar de outro objeto Javascript, o EventEmitter do Node.js em particular, mas realmente qualquer objeto em geral, você precisa fazer duas coisas:
[[proto]]
para objetos criados a partir de seu construtor; no caso de você estar herdando de algum outro objeto, provavelmente deseja usar uma instância do outro objeto como seu protótipo.Isso é mais complicado em Javascript do que pode parecer em outras linguagens porque
Para o caso específico do EventEmitter do Node.js, aqui está o que funciona:
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);
Possíveis pontos fracos:
util.inherits
, mas não chamar o super construtor ( EventEmitter
) para instâncias de sua classe, eles não serão inicializados corretamente.new EventEmitter
) em Master.prototype
vez de fazer o construtor da subclasse Master
chamar o superconstrutor EventEmitter
; dependendo do comportamento do construtor da superclasse, pode parecer que está funcionando bem por um tempo, mas não é a mesma coisa (e não funcionará para EventEmitter).Master.prototype = EventEmitter.prototype
) em vez de adicionar uma camada adicional de objeto via Object.create; pode parecer que está funcionando bem até que alguém faça um monkeypatch em seu objeto Master
e inadvertidamente também faça um monkeypatch EventEmitter
e todos os seus outros descendentes. Cada "classe" deve ter seu próprio protótipo.Novamente: para herdar de EventEmitter (ou realmente qualquer "classe" de objeto existente), você deseja definir um construtor que se encadeia ao superconstrutor e fornece um protótipo que é derivado do superprotótipo.
É assim que a herança prototípica (prototípica?) É feita em JavaScript. Do MDN :
Refere-se ao protótipo do objeto, que pode ser um objeto ou nulo (o que geralmente significa que o objeto é Object.prototype, que não possui protótipo). Às vezes, é usado para implementar pesquisa de propriedade baseada em herança de protótipo.
Isso também funciona:
var Emitter = function(obj) {
this.obj = obj;
}
// DON'T Emitter.prototype = new require('events').EventEmitter();
Emitter.prototype = Object.create(require('events').EventEmitter.prototype);
Noções básicas sobre JavaScript OOP é um dos melhores artigos que li recentemente sobre OOP no ECMAScript 5.
Y.prototype = new X();
é um Y.prototype = Object.create(X.prototype);
new X()
instanciar uma instância de X.prototype
e inicializá-la invocando X
-a. Object.create(X.prototype)
apenas instancia uma instância. Você não quer Emitter.prototype
ser inicializado. Não consigo encontrar um bom artigo que explica isso.
Achei que essa abordagem de http://www.bennadel.com/blog/2187-Extending-EventEmitter-To-Create-An-Evented-Cache-In-Node-js.htm era muito legal:
function EventedObject(){
// Super constructor
EventEmitter.call( this );
return( this );
}
Douglas Crockford também tem alguns padrões de herança interessantes: http://www.crockford.com/javascript/inheritance.html
Acho que a herança é menos frequentemente necessária em JavaScript e Node.js. Mas ao escrever um aplicativo em que a herança pode afetar a escalabilidade, eu consideraria o desempenho avaliado em relação à capacidade de manutenção. Caso contrário, eu apenas basearia minha decisão em quais padrões levam a melhores designs gerais, são mais fáceis de manter e menos sujeitos a erros.
Teste padrões diferentes em jsPerf, usando o Google Chrome (V8) para obter uma comparação aproximada. V8 é o mecanismo JavaScript usado tanto pelo Node.js quanto pelo Chrome.
Aqui estão alguns jsPerfs para você começar:
http://jsperf.com/prototypes-vs-functions/4
emit
e on
estão aparecendo como indefinidas.
Para adicionar à resposta do wprl. Ele perdeu a parte do "protótipo":
function EventedObject(){
// Super constructor
EventEmitter.call(this);
return this;
}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part
util.inherits
já que muitas pessoas inteligentes manterão essas opções atualizadas para você.