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á Masterherdar 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 EventEmitteraula 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').EventEmitterprimeiro - 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 )
MasterInstancedeveria 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.prototypevez de fazer o construtor da subclasse Masterchamar 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 Mastere inadvertidamente também faça um monkeypatch EventEmittere 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.prototypee inicializá-la invocando X-a. Object.create(X.prototype)apenas instancia uma instância. Você não quer Emitter.prototypeser 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
emite onestã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.inheritsjá que muitas pessoas inteligentes manterão essas opções atualizadas para você.