我在很多Node.js库中看到了这种模式:
Master.prototype.__proto__ = EventEmitter.prototype;
(来源here)
有人可以通过一个例子向我解释一下,为什么这是一种常见的模式,什么时候它很方便?
答案 0 :(得分:79)
正如代码上面的评论所说,它会使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);
答案 1 :(得分:69)
这些直接来自文档,但我认为很高兴将它们添加到这个热门问题中供所有人观看。
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {
constructor() {
super(); //must call super for "this" to be defined.
}
}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
我希望git thank
添加任何人。 Event Emitter
注意:文档未在构造函数中调用super()
,这将导致this
未定义。请参阅此issue。
答案 2 :(得分:38)
要继承另一个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添加额外的对象层;这可能看起来好像工作正常,直到有人对你的对象进行monkeypatches Master
,并且无意中也是monkeypatched EventEmitter
及其所有其他后代。每个"班级"应该有自己的原型。再次:要继承EventEmitter(或实际上任何现有对象" class"),你想要定义一个链接到超级构造函数的构造函数,并提供一个派生自超级原型的原型。 / p>
答案 3 :(得分:19)
这是在JavaScript中完成原型(prototypal?)继承的方式。 来自MDN:
指对象的原型,可以是对象或null (通常意味着对象是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);
Understanding JavaScript OOP是我最近在ECMAScript 5中阅读OOP的最佳文章之一。
答案 4 :(得分: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是Node.js和Chrome使用的JavaScript引擎。
这里有一些jsPerfs可以帮助你入门:
http://jsperf.com/prototypes-vs-functions/4
答案 5 :(得分:1)
添加到wprl的响应。他错过了“原型”部分:
function EventedObject(){
// Super constructor
EventEmitter.call(this);
return this;
}
EventObject.prototype = new EventEmitter(); //<-- you're missing this part