你应该如何inheritanceEventEmitter节点?

我正在阅读这篇小文章,以了解从EventEmitterinheritance,但我有点困惑。

他这样做:

 function Door() { events.EventEmitter.call(this); this.open = function() { this.emit('open'); }; } Door.prototype.__proto__ = events.EventEmitter.prototype; 

https://gist.github.com/chevex/7646362

为什么他用自己的构造函数手动调用EventEmitter的构造函数呢? 另外,为什么他将原型的原型设置为EventEmitter的原型呢? 这对我来说非常混乱。

然后在评论中有人build议他这样做,而这看起来更优雅:

 function Door() { events.EventEmitter.call(this); this.open = function () { this.emit('open'); } } util.inherits(Door, events.EventEmitter); 

https://gist.github.com/chevex/7646447

这似乎比其他方式更干净,尽pipe这可能只是因为我不明白发生了什么事情。 如果util.inherits和第一个例子一样,我也不会感到惊讶。

第二个至less对我有点意义,但是我还是不明白为什么他们不这样做:

 function Door() { this.open = function () { this.emit('open'); } } Door.prototype = new events.EventEmitter(); 

https://gist.github.com/chevex/7646524

任何人都可以向我解释所有这些方法之间的区别是什么,为什么在前两个他们调用EventEmitter构造函数EventEmitter .call(this) ? 我在尝试示例时忽略了这一行,他们仍然工作。

第三个示例通常正确:为所有门实例创build一个EventEmitter实例。

让我们设想一个简单的例子:

 var Foo = function() { // each Foo instance has a unique id this.id = Math.random(); } Foo.prototype.doFoo = function() { console.log("Foo!"); } 

假设我们要创build一个从Fooinheritance的Bar构造函数并添加一些新的属性。 如果你遵循你的最后一个例子:

 var Bar = function() { this.something = 5; } Bar.prototype = new Foo(); 

这是错误的,因为所有Bar实例将具有相同的id属性。 相反,我们必须为每个实例调用父构造函数:

 var Bar = function() { Foo.call(this); // set unique `id` on `this` this.something = 5; } Bar.prototype = Object.create(Foo.prototype); 

请注意,这里的最后一行与Bar.prototype.__proto__ = Foo.prototype; 因为Object.create创build一个新的对象,其__proto__被设置为Object.create参数。

为什么他用自己的构造函数手动调用EventEmitter的构造函数呢?

这是确保在EventEmitter构造函数中执行任何代码所必需的。 有些类可能在构造函数中没有做任何有趣的事情,但是其他类将会有重要的代码,所以你应该总是这样做,以确保代码的运行方式与运行新的EventEmitter的方式一样,直接使用var emitter = new EventEmitter;

在某些语言(比如Java)中,这个“构造器链接”types的行为是隐含的,但是在JavaScript中必须明确地完成。

究竟如何设置JavaScript的inheritance归结为“这是复杂”的答案,其他人可能会做得比我更公正。还有几个可行的变化和人们的不同之处是可取的。 然而,仅供参考util.inherits的来源是在这里,最好深入的审查这个我看过的所有口味是在这个video: 道格拉斯Crockford:先进的JavaScript 。 基本上,定期观察,直到它陷入。

寻找参考的地方。 如果你完全理解这些是如何工作的,那么你已经掌握了它(它仍然把我的大脑变成一个椒盐卷饼)

  • Backbone的扩展帮助函数 (Backbone.Model.extend,Backbone.View.extend等)
  • CoffeeScript的类支持
  • node.js的util.extend
 function Door() { events.EventEmitter.call(this); } Door.prototype.__proto__ = events.EventEmitter.prototype; 

在这种情况下,使用events.EventEmitter.call(this)就像在使用super的语言中一样。 实际上在简单的情况下可以省略,但是会破坏当前节点版本的domain支持,也许在将来的版本中会有其他问题。

设置__proto__只是设置了原型链。 也可以这样做:

 Door.prototype = Object.create(events.EventEmitter.prototype); 

但在这种情况下,您还需要手动设置constructor属性。

 util.inherits(Door, events.EventEmitter); 

这是inheritance节点的惯用方式。 所以你最好用这个。 但是它所做的基本上和上面一样。

 function Door() { } Door.prototype = new events.EventEmitter(); 

这是错误的方式,不要使用它! 您将以在一些节点版本中的实例之间共享事件而结束。

Node v6.3.1文档指出了util.inherits(constructor, superConstructor)

不鼓励使用util.inherits() 。 请使用ES6 classextends关键字以获得语言级别的inheritance支持。 另请注意,这两种风格在语义上是不兼容的。


以下代码显示了如何使用Typescript从EventEmitterinheritance:

 import { EventEmitter } from "events" class Person extends EventEmitter { constructor(public name: string) { super() } } let person = new Person("Bob") person.on("speak", function (said: string) { console.log(`${this.name} said: ${said}`) }) person.emit("speak", "'hello'") // prints "Bob said: 'hello'" 

以前的代码将转换成以下ES6代码:

 "use strict"; const events = require("events"); class Person extends events.EventEmitter { constructor(name) { super(); this.name = name; } } let person = new Person("Bob"); person.on("speak", function (said) { console.log(`${this.name} said: ${said}`); }); person.emit("speak", "'hello'");